提交 b64752c2 编写于 作者: J Joshua Lambert

Fix merge conflicts

......@@ -180,6 +180,7 @@ update-knapsack:
<<: *only-canonical-masters
stage: post-test
script:
- retry gem install fog-aws mime-types
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json
- '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
......
......@@ -91,7 +91,7 @@ gem 'carrierwave', '~> 1.1'
gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
gem 'fog-aws', '~> 1.4'
gem 'fog-core', '~> 1.44'
gem 'fog-google', '~> 0.5'
gem 'fog-local', '~> 0.3'
......@@ -237,7 +237,6 @@ gem 'webpack-rails', '~> 0.9.10'
gem 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6'
gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2'
gem 'addressable', '~> 2.3.8'
......
......@@ -122,13 +122,6 @@ GEM
coderay (1.1.1)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
coffee-rails (4.1.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.1.x)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.10.0)
colorize (0.7.7)
concurrent-ruby (1.0.5)
concurrent-ruby-ext (1.0.5)
......@@ -186,7 +179,7 @@ GEM
et-orbi (1.0.3)
tzinfo
eventmachine (1.0.8)
excon (0.55.0)
excon (0.57.1)
execjs (2.6.0)
expression_parser (0.9.0)
extlib (0.9.16)
......@@ -222,26 +215,26 @@ GEM
fog-json (~> 1.0)
ipaddress (~> 0.8)
xml-simple (~> 1.1)
fog-aws (0.13.0)
fog-aws (1.4.0)
fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-core (1.44.1)
fog-core (1.44.3)
builder
excon (~> 0.49)
formatador (~> 0.2)
fog-google (0.5.0)
fog-google (0.5.3)
fog-core
fog-json
fog-xml
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
fog-local (0.3.0)
fog-local (0.3.1)
fog-core (~> 1.27)
fog-openstack (0.1.6)
fog-core (>= 1.39)
fog-openstack (0.1.21)
fog-core (>= 1.40)
fog-json (>= 1.0)
ipaddress (>= 0.8)
fog-rackspace (0.1.1)
......@@ -938,7 +931,6 @@ DEPENDENCIES
charlock_holmes (~> 0.7.3)
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
coffee-rails (~> 4.1.0)
concurrent-ruby (~> 1.0.5)
connection_pool (~> 2.0)
creole (~> 0.5.0)
......@@ -961,7 +953,7 @@ DEPENDENCIES
flipper (~> 0.10.2)
flipper-active_record (~> 0.10.2)
fog-aliyun (~> 0.1.0)
fog-aws (~> 0.9)
fog-aws (~> 1.4)
fog-core (~> 1.44)
fog-google (~> 0.5)
fog-local (~> 0.3)
......
......@@ -9,7 +9,7 @@
## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=rake+karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
## Canonical source
......
......@@ -5,21 +5,28 @@ import './preview_markdown';
window.DropzoneInput = (function() {
function DropzoneInput(form) {
var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile, addFileToForm;
Dropzone.autoDiscover = false;
divHover = '<div class="div-dropzone-hover"></div>';
iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
$attachButton = form.find('.button-attach-file');
$attachingFileMessage = form.find('.attaching-file-message');
$cancelButton = form.find('.button-cancel-uploading-files');
$retryLink = form.find('.retry-uploading-link');
$uploadProgress = form.find('.uploading-progress');
$uploadingErrorContainer = form.find('.uploading-error-container');
$uploadingErrorMessage = form.find('.uploading-error-message');
$uploadingProgressContainer = form.find('.uploading-progress-container');
uploadsPath = window.uploads_path || null;
maxFileSize = gon.max_file_size || 10;
formTextarea = form.find('.js-gfm-input');
const divHover = '<div class="div-dropzone-hover"></div>';
const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
const $attachButton = form.find('.button-attach-file');
const $attachingFileMessage = form.find('.attaching-file-message');
const $cancelButton = form.find('.button-cancel-uploading-files');
const $retryLink = form.find('.retry-uploading-link');
const $uploadProgress = form.find('.uploading-progress');
const $uploadingErrorContainer = form.find('.uploading-error-container');
const $uploadingErrorMessage = form.find('.uploading-error-message');
const $uploadingProgressContainer = form.find('.uploading-progress-container');
const uploadsPath = window.uploads_path || null;
const maxFileSize = gon.max_file_size || 10;
const formTextarea = form.find('.js-gfm-input');
let handlePaste;
let pasteText;
let addFileToForm;
let updateAttachingMessage;
let isImage;
let getFilename;
let uploadFile;
formTextarea.wrap('<div class="div-dropzone"></div>');
formTextarea.on('paste', (function(_this) {
return function(event) {
......@@ -28,16 +35,16 @@ window.DropzoneInput = (function() {
})(this));
// Add dropzone area to the form.
$mdArea = formTextarea.closest('.md-area');
const $mdArea = formTextarea.closest('.md-area');
form.setupMarkdownPreview();
$formDropzone = form.find('.div-dropzone');
const $formDropzone = form.find('.div-dropzone');
$formDropzone.parent().addClass('div-dropzone-wrapper');
$formDropzone.append(divHover);
$formDropzone.find('.div-dropzone-hover').append(iconPaperclip);
if (!uploadsPath) return;
dropzone = $formDropzone.dropzone({
const dropzone = $formDropzone.dropzone({
url: uploadsPath,
dictDefaultMessage: '',
clickable: true,
......@@ -117,7 +124,7 @@ window.DropzoneInput = (function() {
}
});
child = $(dropzone[0]).children('textarea');
const child = $(dropzone[0]).children('textarea');
// removeAllFiles(true) stops uploading files (if any)
// and remove them from dropzone files queue.
......@@ -214,6 +221,35 @@ window.DropzoneInput = (function() {
return value.first();
};
const showSpinner = function(e) {
return $uploadingProgressContainer.removeClass('hide');
};
const closeSpinner = function() {
return $uploadingProgressContainer.addClass('hide');
};
const showError = function(message) {
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
};
const closeAlertMessage = function() {
return form.find('.div-dropzone-alert').alert('close');
};
const insertToTextArea = function(filename, url) {
return $(child).val(function(index, val) {
return val.replace(`{{${filename}}}`, url);
});
};
const appendToTextArea = function(url) {
return $(child).val(function(index, val) {
return val + url + "\n";
});
};
uploadFile = function(item, filename) {
var formData;
formData = new FormData();
......@@ -262,35 +298,6 @@ window.DropzoneInput = (function() {
messageContainer.text(attachingMessage);
};
insertToTextArea = function(filename, url) {
return $(child).val(function(index, val) {
return val.replace(`{{${filename}}}`, url);
});
};
appendToTextArea = function(url) {
return $(child).val(function(index, val) {
return val + url + "\n";
});
};
showSpinner = function(e) {
return $uploadingProgressContainer.removeClass('hide');
};
closeSpinner = function() {
return $uploadingProgressContainer.addClass('hide');
};
showError = function(message) {
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
};
closeAlertMessage = function() {
return form.find('.div-dropzone-alert').alert('close');
};
form.find('.markdown-selector').click(function(e) {
e.preventDefault();
$(this).closest('.gfm-form').find('.div-dropzone').click();
......
......@@ -3,6 +3,7 @@
*/
export default {
ABORTED: 0,
NO_CONTENT: 204,
OK: 200,
};
......@@ -81,6 +81,9 @@ export default class Poll {
})
.catch((error) => {
notificationCallback(false);
if (error.status === httpStatusCodes.ABORTED) {
return;
}
errorCallback(error);
});
}
......
......@@ -297,18 +297,12 @@
}
.droplab-dropdown {
.description {
display: inline-block;
white-space: normal;
margin-left: 5px;
}
.dropdown-toggle > i {
pointer-events: none;
}
li {
padding: $gl-btn-padding $gl-btn-padding 2px;
.dropdown-menu li {
padding: $gl-btn-padding;
cursor: pointer;
> a,
......@@ -344,9 +338,25 @@
visibility: visible;
}
&.divider {
margin: 0 8px;
padding: 0;
border-top: $gray-darkest;
}
.icon {
visibility: hidden;
}
.description {
display: inline-block;
white-space: normal;
margin-left: 5px;
p {
margin-bottom: 0;
}
}
}
.icon {
......@@ -354,12 +364,6 @@
vertical-align: top;
padding-top: 2px;
}
.divider {
margin: 0 8px;
padding: 0;
border-top: $gray-darkest;
}
}
.droplab-dropdown .dropdown-menu,
......
......@@ -275,7 +275,7 @@
}
.filtered-search-input-dropdown-menu {
max-height: 215px;
max-height: 225px;
max-width: 280px;
overflow: auto;
......@@ -382,10 +382,6 @@
}
}
.dropdown-menu .filter-dropdown-item {
padding: 0;
}
@media (max-width: $screen-xs-max) {
.issues-details-filters {
padding: 0 0 10px;
......@@ -435,6 +431,7 @@
.fa {
width: 15px;
line-height: $line-height-base;
}
.dropdown-label-box {
......
......@@ -2,6 +2,10 @@
color: $gl-text-color;
word-wrap: break-word;
[dir="auto"] {
text-align: initial;
}
a {
color: $md-link-color;
}
......
......@@ -110,10 +110,10 @@ $well-light-text-color: #5b6169;
* Text
*/
$gl-font-size: 14px;
$gl-text-color: rgba(0, 0, 0, .85);
$gl-text-color-light: rgba(0, 0, 0, .7);
$gl-text-color-secondary: rgba(0, 0, 0, .55);
$gl-text-color-disabled: rgba(0, 0, 0, .35);
$gl-text-color: #2e2e2e;
$gl-text-color-secondary: #707070;
$gl-text-color-tertiary: #949494;
$gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1.0);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
$gl-text-green: $green-600;
......@@ -127,7 +127,7 @@ $gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54;
$gl-header-nav-hover-color: #434343;
$placeholder-text-color: rgba(0, 0, 0, .42);
$placeholder-text-color: $gl-text-color-tertiary;
/*
* Lists
......@@ -135,7 +135,7 @@ $placeholder-text-color: rgba(0, 0, 0, .42);
$list-font-size: $gl-font-size;
$list-title-color: $gl-text-color;
$list-text-color: $gl-text-color;
$list-text-disabled-color: $gl-text-color-disabled;
$list-text-disabled-color: $gl-text-color-tertiary;
$list-border-light: #eee;
$list-border: rgba(0, 0, 0, 0.05);
$list-text-height: 42px;
......
......@@ -284,7 +284,7 @@ header.navbar-gitlab-new {
position: relative;
top: -1px;
padding: 0 5px;
color: rgba($black, .65);
color: $gl-text-color-secondary;
font-size: 10px;
line-height: 1;
background: none;
......@@ -310,10 +310,10 @@ header.navbar-gitlab-new {
.breadcrumbs-links {
flex: 1;
align-self: center;
color: $black-transparent;
color: $gl-text-color-quaternary;
a {
color: rgba($black, .65);
color: $gl-text-color-secondary;
&:not(:first-child),
&.group-path {
......@@ -368,9 +368,10 @@ header.navbar-gitlab-new {
}
.breadcrumbs-sub-title {
margin: 2px 0 0;
margin: 2px 0;
font-size: 16px;
font-weight: normal;
line-height: 1;
ul {
margin: 0;
......
......@@ -35,6 +35,7 @@ $new-sidebar-width: 220px;
.avatar-container {
flex: 0 0 40px;
background-color: $white-light;
}
&:hover {
......
......@@ -813,8 +813,6 @@
}
.description {
margin-bottom: 10px;
.text {
margin: 0;
}
......
class Commit
extend ActiveModel::Naming
extend Gitlab::Cache::RequestCache
include ActiveModel::Conversion
include Noteable
......@@ -169,19 +170,9 @@ class Commit
end
def author
if RequestStore.active?
key = "commit_author:#{author_email.downcase}"
# nil is a valid value since no author may exist in the system
if RequestStore.store.key?(key)
@author = RequestStore.store[key]
else
@author = find_author_by_any_email
RequestStore.store[key] = @author
end
else
@author ||= find_author_by_any_email
end
User.find_by_any_email(author_email.downcase)
end
request_cache(:author) { author_email.downcase }
def committer
@committer ||= User.find_by_any_email(committer_email.downcase)
......@@ -322,7 +313,7 @@ class Commit
def raw_diffs(*args)
if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
Gitlab::GitalyClient::Commit.new(project.repository).diff_from_parent(self, *args)
Gitlab::GitalyClient::CommitService.new(project.repository).diff_from_parent(self, *args)
else
raw.diffs(*args)
end
......@@ -331,7 +322,7 @@ class Commit
def raw_deltas
@deltas ||= Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled|
if is_enabled
Gitlab::GitalyClient::Commit.new(project.repository).commit_deltas(self)
Gitlab::GitalyClient::CommitService.new(project.repository).commit_deltas(self)
else
raw.deltas
end
......@@ -368,10 +359,6 @@ class Commit
end
end
def find_author_by_any_email
User.find_by_any_email(author_email.downcase)
end
def repo_changes
changes = { added: [], modified: [], removed: [] }
......
......@@ -2,7 +2,6 @@ require 'carrierwave/orm/activerecord'
class Group < Namespace
include Gitlab::ConfigHelper
include Gitlab::VisibilityLevel
include AccessRequestable
include Avatarable
include Referable
......@@ -103,10 +102,6 @@ class Group < Namespace
full_name
end
def visibility_level_field
:visibility_level
end
def visibility_level_allowed_by_projects
allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none?
......
......@@ -5,6 +5,7 @@ class Namespace < ActiveRecord::Base
include Sortable
include Gitlab::ShellAdapter
include Gitlab::CurrentSettings
include Gitlab::VisibilityLevel
include Routable
include AfterCommitQueue
......@@ -105,6 +106,10 @@ class Namespace < ActiveRecord::Base
end
end
def visibility_level_field
:visibility_level
end
def to_param
full_path
end
......
......@@ -190,7 +190,7 @@ class Note < ActiveRecord::Base
# override to return commits, which are not active record
def noteable
if for_commit?
project.commit(commit_id)
@commit ||= project.commit(commit_id)
else
super
end
......
......@@ -3,9 +3,13 @@ module Ci
condition(:protected_action) do
next false unless @subject.action?
!::Gitlab::UserAccess
.new(@user, project: @subject.project)
.can_merge_to_branch?(@subject.ref)
access = ::Gitlab::UserAccess.new(@user, project: @subject.project)
if @subject.tag?
!access.can_create_tag?(@subject.ref)
else
!access.can_merge_to_branch?(@subject.ref)
end
end
rule { protected_action }.prevent :update_build
......
......@@ -55,7 +55,22 @@
%span
Members
- if current_user && can?(current_user, :admin_group, @group)
= nav_link(path: %w[groups#projects groups#edit]) do
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
= link_to edit_group_path(@group), title: 'Settings' do
%span
Settings
%ul.sidebar-sub-level-items
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), title: 'General' do
%span
General
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: 'Projects' do
%span
Projects
= nav_link(controller: :ci_cd) do
= link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do
%span
Pipelines
......@@ -165,7 +165,7 @@
Snippets
- if project_nav_tab? :settings
= nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span
Settings
......@@ -177,8 +177,8 @@
= link_to edit_project_path(@project), title: 'General' do
%span
General
= nav_link(controller: :members) do
= link_to project_settings_members_path(@project), title: 'Members' do
= nav_link(controller: :project_members) do
= link_to project_project_members_path(@project), title: 'Members' do
%span
Members
- if can_edit
......
......@@ -9,8 +9,8 @@
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
.landing.content-block{ "v-if" => "!isOverviewDialogDismissed" }
%button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box' }
= icon("times", "@click" => "dismissOverviewDialog()")
%button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box', "@click" => "dismissOverviewDialog()" }
= icon("times")
.svg-container
= custom_icon('icon_cycle_analytics_splash')
.inner-content
......
......@@ -98,7 +98,7 @@
- if type == :boards
- if can?(current_user, :admin_list, @project)
.dropdown.prepend-left-10#js-add-list
%button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path) } }
%button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) } }
Add list
.dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
......
......@@ -2,6 +2,8 @@ class ProjectServiceWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
sidekiq_options dead: false
def perform(hook_id, data)
data = data.with_indifferent_access
Service.find(hook_id).execute(data)
......
......@@ -2,7 +2,7 @@ class WebHookWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
sidekiq_options retry: 4
sidekiq_options retry: 4, dead: false
def perform(hook_id, data, hook_name)
hook = WebHook.find(hook_id)
......
---
title: "reset text-align to initial to let elements with dir="auto" align texts to right in RTL languages ( default is left )"
merge_request: 12892
author: goshhob
---
title: refactor initializations in dropzone_input.js
merge_request: 12768
author: Brandon Everett
---
title: Prevent web hook and project service background jobs from going to the dead
jobs queue
merge_request:
author:
---
title: Remove coffee-rails gem
merge_request:
author: Takuya Noguchi
---
title: Protect manual actions against protected tag too
merge_request: 12908
author:
---
title: Bump fog-core to 1.44.3 and fog providers' plugins to latest
merge_request: 12897
author: Takuya Noguchi
---
title: allow closing Cycle Analytics intro box in firefox
merge_request:
author:
---
title: Fix label creation from new list for subgroup projects
merge_request:
author:
---
title: fix transient js error in rspec tests
merge_request:
author:
---
title: Add RequestCache which makes caching with RequestStore easier
merge_request: 12920
author:
......@@ -33,7 +33,6 @@ module GettextI18nRailsJs
[
".js",
".jsx",
".coffee",
".vue"
].include? ::File.extname(file)
end
......
......@@ -7,7 +7,7 @@
- aws_elb_request_count_sum
weight: 1
queries:
- query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) * 60'
- query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60'
label: Total
unit: req / sec
- title: "Latency"
......
class AddForeignKeyToMergeRequests < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
class MergeRequest < ActiveRecord::Base
self.table_name = 'merge_requests'
include ::EachBatch
end
def up
scope = <<-SQL.strip_heredoc
head_pipeline_id IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM ci_pipelines
WHERE ci_pipelines.id = merge_requests.head_pipeline_id
)
SQL
MergeRequest.where(scope).each_batch(of: 1000) do |merge_requests|
merge_requests.update_all(head_pipeline_id: nil)
end
unless foreign_key_exists?(:merge_requests, :head_pipeline_id)
add_concurrent_foreign_key(:merge_requests, :ci_pipelines,
column: :head_pipeline_id, on_delete: :nullify)
end
end
def down
if foreign_key_exists?(:merge_requests, :head_pipeline_id)
remove_foreign_key(:merge_requests, column: :head_pipeline_id)
end
end
private
def foreign_key_exists?(table, column)
foreign_keys(table).any? do |key|
key.options[:column] == column.to_s
end
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170707184244) do
ActiveRecord::Schema.define(version: 20170713104829) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -1615,6 +1615,7 @@ ActiveRecord::Schema.define(version: 20170707184244) do
add_foreign_key "merge_request_diffs", "merge_requests", name: "fk_8483f3258f", on_delete: :cascade
add_foreign_key "merge_request_metrics", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade
add_foreign_key "merge_requests", "ci_pipelines", column: "head_pipeline_id", name: "fk_fd82eae0b9", on_delete: :nullify
add_foreign_key "merge_requests", "projects", column: "target_project_id", name: "fk_a6963e8447", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade
......
......@@ -50,8 +50,7 @@ Shortcuts to GitLab's most visited docs:
- [Fork a project](gitlab-basics/fork-project.md)
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
- [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private.
- [Groups](workflow/groups.md): Organize your projects in groups.
- [Create a group](gitlab-basics/create-group.md)
- [Groups](user/group/index.md): Organize your projects in groups.
- [GitLab Subgroups](user/group/subgroups/index.md)
- [Search through GitLab](user/search/index.md): Search for issues, merge requests, projects, groups, todos, and issues in Issue Boards.
- [Snippets](user/snippets.md): Snippets allow you to create little bits of code.
......
......@@ -228,9 +228,14 @@ Tip: If you want to limit access to the nested members of an Active Directory
group you can use the following syntax:
```
(memberOf=CN=My Group,DC=Example,DC=com)
(memberOf:1.2.840.113556.1.4.1941=CN=My Group,DC=Example,DC=com)
```
Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx. Support for
nested members in the user filter should not be confused with
[group sync nested groups support (EE only)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#supported-ldap-group-types-attributes).
Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap.
......
# IP whitelist
> Introduced in GitLab 9.4.
GitLab provides some [monitoring endpoints] that provide health check information
when probed.
To control access to those endpoints via IP whitelisting, you can add single
hosts or use IP ranges:
**For Omnibus installations**
1. Open `/etc/gitlab/gitlab.rb` and add or uncomment the following:
```ruby
gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.0.1']
```
1. Save the file and [reconfigure] GitLab for the changes to take effect.
---
**For installations from source**
1. Edit `config/gitlab.yml`:
```yaml
monitoring:
# by default only local IPs are allowed to access monitoring resources
ip_whitelist:
- 127.0.0.0/8
- 192.168.0.1
```
1. Save the file and [restart] GitLab for the changes to take effect.
[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: ../restart_gitlab.md#installations-from-source
[monitoring endpoints]: ../../user/admin_area/monitoring/health_check.md
# GitLab Prometheus metrics
>**Note:**
Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For installations from source
you'll have to configure it yourself.
GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other [Prometheus] exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic.
Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For
installations from source you'll have to configure it yourself.
To enable the GitLab Prometheus metrics:
......@@ -15,9 +13,14 @@ To enable the GitLab Prometheus metrics:
## Collecting the metrics
Since the metrics endpoint is available on the same host and port as other traffic, it requires authentication. The token and URL to access is displayed on the [Health Check][health-check] page.
GitLab monitors its own internal service metrics, and makes them available at the
`/-/metrics` endpoint. Unlike other [Prometheus] exporters, in order to access
it, the client IP needs to be [included in a whitelist][whitelist].
Currently the embedded Prometheus server is not automatically configured to collect metrics from this endpoint. We recommend setting up another Prometheus server, because the embedded server configuration is overwritten one every reconfigure of GitLab. In the future this will not be required.
Currently the embedded Prometheus server is not automatically configured to
collect metrics from this endpoint. We recommend setting up another Prometheus
server, because the embedded server configuration is overwritten once every
[reconfigure of GitLab][reconfigure]. In the future this will not be required.
## Metrics available
......@@ -47,4 +50,5 @@ In this experimental phase, only a few metrics are available:
[29118]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29118
[Prometheus]: https://prometheus.io
[restart]: ../../restart_gitlab.md#omnibus-gitlab-restart
[health-check]: ../../../user/admin_area/monitoring/health_check.md
[whitelist]: ../ip_whitelist.md
[reconfigure]: ../../restart_gitlab.md#omnibus-gitlab-reconfigure
......@@ -95,8 +95,9 @@ Sample Prometheus queries:
## Configuring Prometheus to monitor Kubernetes
> Introduced in GitLab 9.0.
> Pod monitoring introduced in GitLab 9.4.
If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes in the cluster including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration][] to monitor them.
If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes and [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>) in the cluster, including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration][] to monitor them.
To disable the monitoring of Kubernetes:
......
......@@ -388,8 +388,8 @@ the style below as a guide:
1. Save the file and [restart] GitLab for the changes to take effect.
[reconfigure]: path/to/administration/gitlab_restart.md#omnibus-gitlab-reconfigure
[restart]: path/to/administration/gitlab_restart.md#installations-from-source
[reconfigure]: path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: path/to/administration/restart_gitlab.md#installations-from-source
````
In this case:
......
......@@ -6,7 +6,7 @@ Step-by-step guides on the basics of working with Git and GitLab.
- [Start using Git on the command line](start-using-git.md)
- [Create and add your SSH Keys](create-your-ssh-keys.md)
- [Create a project](create-project.md)
- [Create a group](create-group.md)
- [Create a group](../user/group/index.md#create-a-new-group)
- [Create a branch](create-branch.md)
- [Fork a project](fork-project.md)
- [Add a file](add-file.md)
......
# How to create a group in GitLab
Your projects in GitLab can be organized in 2 different ways:
under your own namespace for single projects, such as `your-name/project-1` or
under groups.
If you organize your projects under a group, it works like a folder. You can
manage your group members' permissions and access to the projects.
---
To create a group:
1. Expand the left sidebar by clicking the three bars at the upper left corner
and then navigate to **Groups**.
![Go to groups](img/create_new_group_sidebar.png)
1. Once in your groups dashboard, click on **New group**.
![Create new group information](img/create_new_group_info.png)
1. Fill out the needed information:
1. Set the "Group path" which will be the namespace under which your projects
will be hosted (path can contain only letters, digits, underscores, dashes
and dots; it cannot start with dashes or end in dot).
1. The "Group name" will populate with the path. Optionally, you can change
it. This is the name that will display in the group views.
1. Optionally, you can add a description so that others can briefly understand
what this group is about.
1. Optionally, choose and avatar for your project.
1. Choose the [visibility level](../public_access/public_access.md).
1. Finally, click the **Create group** button.
## Add a new project to a group
There are 2 different ways to add a new project to a group:
- Select a group and then click on the **New project** button.
![New project](img/create_new_project_from_group.png)
You can then continue on [creating a project](create-project.md).
- While you are [creating a project](create-project.md), select a group namespace
you've already created from the dropdown menu.
![Select group](img/select_group_dropdown.png)
This document was moved to [another location](../user/group/index.md#create-a-new-group).
......@@ -2,13 +2,13 @@
![GCP landing page](img/gcp_landing.png)
>**Important note:**
GitLab has no official images in Google Cloud Platform yet. This guide serves
as a template for when the GitLab VM will be available.
The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through
the [Google Cloud Launcher][launcher] program.
GitLab's official Google Launcher apps:
1. [GitLab Community Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-community-edition?project=gitlab-public)
2. [GitLab Enterprise Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-enterprise-edition?project=gitlab-public)
## Prerequisites
There are only two prerequisites in order to install GitLab on GCP:
......
......@@ -157,8 +157,7 @@ configuration file may contain syntax errors. The block name
file, should be `[[storage]]` instead.
```shell
cd /home/git/gitaly
sudo -u git -H editor config.toml
sudo -u git -H sed -i.pre-9.4 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
```
#### Compile Gitaly
......
......@@ -6,7 +6,7 @@
be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
section.
- [Access token](#access-token) has been deprecated in GitLab 9.4
in favor of [IP Whitelist](#ip-whitelist)
in favor of [IP whitelist](#ip-whitelist)
GitLab provides liveness and readiness probes to indicate service health and
reachability to required services. These probes report on the status of the
......@@ -14,109 +14,101 @@ database connection, Redis connection, and access to the filesystem. These
endpoints [can be provided to schedulers like Kubernetes][kubernetes] to hold
traffic until the system is ready or restart the container as needed.
## IP Whitelist
## IP whitelist
To access monitoring resources the client IP needs to be included in the whitelist.
To add or remove hosts or IP ranges from the list you can edit `gitlab.rb` or `gitlab.yml`.
To access monitoring resources, the client IP needs to be included in a whitelist.
Example whitelist configuration:
```yaml
monitoring:
ip_whitelist:
- 127.0.0.0/8 # by default only local IPs are allowed to access monitoring resources
```
[Read how to add IPs to a whitelist for the monitoring endpoints.][admin].
## Access Token (Deprecated)
## Using the endpoint
An access token needs to be provided while accessing the probe endpoints. The current
accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check**
(`admin/health_check`) page of your GitLab instance.
With default whitelist settings, the probes can be accessed from localhost:
![access token](img/health_check_token.png)
- `http://localhost/-/readiness`
- `http://localhost/-/liveness`
The access token can be passed as a URL parameter:
which will then provide a report of system health in JSON format.
Readiness example output:
```
https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN
{
"queues_check" : {
"status" : "ok"
},
"redis_check" : {
"status" : "ok"
},
"shared_state_check" : {
"status" : "ok"
},
"fs_shards_check" : {
"labels" : {
"shard" : "default"
},
"status" : "ok"
},
"db_check" : {
"status" : "ok"
},
"cache_check" : {
"status" : "ok"
}
}
```
which will then provide a report of system health in JSON format:
Liveness example output:
```
{
"db_check": {
"status": "ok"
},
"redis_check": {
"status": "ok"
},
"fs_shards_check": {
"status": "ok",
"labels": {
"shard": "default"
}
}
"fs_shards_check" : {
"status" : "ok"
},
"cache_check" : {
"status" : "ok"
},
"db_check" : {
"status" : "ok"
},
"redis_check" : {
"status" : "ok"
},
"queues_check" : {
"status" : "ok"
},
"shared_state_check" : {
"status" : "ok"
}
}
```
## Using the Endpoint
With default whitelist settings, the probes can be accessed from localhost:
- `http://localhost/-/readiness`
- `http://localhost/-/liveness`
## Status
On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
will return a valid successful HTTP status code, and a `success` message.
## Old behavior
## Access token (Deprecated)
>**Notes:**
- Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
- The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and will
be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
section.
GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
endpoint. The health check reports on the overall system status based on the status of
the database connection, the state of the database migrations, and the ability to write
and access the cache. This endpoint can be provided to uptime monitoring services like
[Pingdom][pingdom], [Nagios][nagios-health], and [NewRelic][newrelic-health].
Once you have the [access token](#access-token) or your client IP is [whitelisted](#ip-whitelist),
health information can be retrieved as plain text, JSON, or XML using the `health_check` endpoint:
- `https://gitlab.example.com/health_check?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check.xml?token=ACCESS_TOKEN`
You can also ask for the status of specific services:
- `https://gitlab.example.com/health_check/cache.json?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check/database.json?token=ACCESS_TOKEN`
- `https://gitlab.example.com/health_check/migrations.json?token=ACCESS_TOKEN`
>**Note:**
Access token has been deprecated in GitLab 9.4
in favor of [IP whitelist](#ip-whitelist)
For example, the JSON output of the following health check:
An access token needs to be provided while accessing the probe endpoints. The current
accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check**
(`admin/health_check`) page of your GitLab instance.
```bash
curl --header "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
```
![access token](img/health_check_token.png)
would be like:
The access token can be passed as a URL parameter:
```
{"healthy":true,"message":"success"}
https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN
```
On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
will return a valid successful HTTP status code, and a `success` message. Ideally your
uptime monitoring should look for the success message.
[ce-10416]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10416
[ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
[pingdom]: https://www.pingdom.com
[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
[newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring
[kubernetes]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
[admin]: ../../../administration/monitoring/ip_whitelist.md
# Groups
With GitLab Groups you can assemble related projects together
and grant members access to several projects at once.
Groups can also be nested in [subgroups](subgroups/index.md).
Find your groups by expanding the left menu and clicking **Groups**:
![GitLab Groups](img/groups.png)
The Groups page displays all groups you are a member of, how many projects it holds,
how many members it has, the group visibility, and, if you have enough permissions,
a link to the group settings. By clicking the last button you can leave that group.
## Use cases
You can create groups for numerous reasons. To name a few:
- Organize related projects under the same [namespace](#namespaces), add members to that
group and grant access to all their projects at once
- Create a group, include members of your team, and make it easier to
`@mention` all the team at once in issues and merge requests
- Create a group for your company members, and create [subgroups](subgroups/index.md)
for each individual team. Let's say you create a group called `company-team`, and among others,
you created subgroups in this group for each individual team `backend-team`,
`frontend-team`, and `production-team`:
1. When you start a new implementation from an issue, you add a comment:
_"`@company-team`, let's do it! `@company-team/backend-team` you're good to go!"_
1. When your backend team needs help from frontend, they add a comment:
_"`@company-team/frontend-team` could you help us here please?"_
1. When the frontend team completes their implementation, they comment:
_"`@company-team/backend-team`, it's done! Let's ship it `@company-team/production-team`!"_
## Namespaces
In GitLab, a namespace is a unique name to be used as a user name, a group name, or a subgroup name.
- `http://gitlab.example.com/username`
- `http://gitlab.example.com/groupname`
- `http://gitlab.example.com/groupname/subgroup_name`
For example, consider a user called John:
1. John creates his account on GitLab.com with the username `jonh`;
his profile will be accessed under `https://gitlab.example.com/john`
1. John creates a group for his team with the groupname `john-team`;
his group and its projects will be accessed under `https://gitlab.example.com/john-team`
1. John creates a subgroup of `john-team` with the subgroup name `marketing`;
his subgroup and its projects will be accessed under `https://gitlab.example.com/john-team/marketing`
By doing so:
- Any team member mentions John with `@john`
- John mentions everyone from his team with `@john-team`
- John mentions only his marketing team with `@john-team/marketing`
## Create a new group
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**:
![new group from groups page](img/new_group_from_groups.png)
1. Elsewhere: expand the `plus` sign button on the top navbar and choose **New group**:
![new group from elsewhere](img/new_group_from_other_pages.png)
Add the following information:
![new group info](img/create_new_group_info.png)
1. Set the **Group path** which will be the **namespace** under which your projects
will be hosted (path can contain only letters, digits, underscores, dashes
and dots; it cannot start with dashes or end in dot).
1. The **Group name** will populate with the path. Optionally, you can change
it. This is the name that will display in the group views.
1. Optionally, you can add a description so that others can briefly understand
what this group is about.
1. Optionally, choose an avatar for your project.
1. Choose the [visibility level](../../public_access/public_access.md).
## Add users to a group
Add members to a group by navigating to the group's dashboard, and clicking **Members**:
![add members to group](img/add_new_members.png)
Select the [permission level][permissions] and add the new member. You can also set the expiring
date for that user, from which they will no longer have access to your group.
One of the benefits of putting multiple projects in one group is that you can
give a user to access to all projects in the group with one action.
Consider we have a group with two projects:
- On the **Group Members** page we can now add a new user to the group.
- Now because this user is a **Developer** member of the group, he automatically
gets **Developer** access to **all projects** within that group.
If necessary, you can increase the access level of an individual user for a specific project,
by adding them again as a new member to the project with the new permission levels.
## Request access to a group
As a group owner you can enable or disable non members to request access to
your group. Go to the group settings and click on **Allow users to request access**.
As a user, you can request to be a member of a group. Go to the group you'd
like to be a member of, and click the **Request Access** button on the right
side of your screen.
![Request access button](img/request_access_button.png)
---
Group owners and masters will be notified of your request and will be able to approve or
decline it on the members page.
![Manage access requests](img/access_requests_management.png)
---
If you change your mind before your request is approved, just click the
**Withdraw Access Request** button.
![Withdraw access request button](img/withdraw_access_request_button.png)
## Add projects to a group
There are two different ways to add a new project to a group:
- Select a group and then click on the **New project** button.
![New project](img/create_new_project_from_group.png)
You can then continue on [creating a project](../../gitlab-basics/create-project.md).
- While you are creating a project, select a group namespace
you've already created from the dropdown menu.
![Select group](img/select_group_dropdown.png)
## Transfer an existing project into a group
You can transfer an existing project into a group as long as you have at least **Master** [permissions][permissions] to that group
and if you are an **Owner** of the project.
![Transfer a project to a new namespace](img/transfer_project_to_other_group.png)
Find this option under your project's settings.
GitLab administrators can use the admin interface to move any project to any namespace if needed.
## Manage group memberships via LDAP
In GitLab Enterprise Edition it is possible to manage GitLab group memberships using LDAP groups.
See [the GitLab Enterprise Edition documentation](../../integration/ldap.md) for more information.
## Group settings
Once you have created a group, you can manage its settings by navigating to
the group's dashboard, and clicking **Settings**.
![group settings](img/group_settings.png)
### General settings
Besides giving you the option to edit any settings you've previously
set when [creating the group](#create-a-new-group), you can also
access further configurations for your group.
#### Enforce 2FA to group members
Add a secury layer to your group by
[enforcing two-factor authentication (2FA)](../../security/two_factor_authentication.md#enforcing-2fa-for-all-users-in-a-group)
to all group members.
#### Member Lock (EES/EEP)
Available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/),
with **Member Lock** it is possible to lock membership in project to the
level of members in group.
Learn more about [Member Lock](https://docs.gitlab.com/ee/user/group/index.html#member-lock-ees-eep).
#### Share with group lock (EES/EEP)
In [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)
it is possible to prevent projects in a group from [sharing
a project with another group](../../workflow/share_projects_with_other_groups.md).
This allows for tighter control over project access.
Learn more about [Share with group lock](https://docs.gitlab.com/ee/user/group/index.html#share-with-group-lock-ees-eep).
### Advanced settings
- **Projects**: view all projects within that group, add members to each project,
access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md)
and [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group (Push Rules is available in [GitLab Enteprise Edition Starter][ee].)
- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
for the group (GitLab admins only, available in [GitLab Enterprise Edition Starter][ee]).
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
[permissions]: ../permissions.md#permissions
[ee]: https://about.gitlab.com/products/
\ No newline at end of file
......@@ -40,7 +40,7 @@ Installing and configuring Prometheus to monitor applications is fairly straight
### Configuring Omnibus GitLab Prometheus to monitor Kubernetes deployments
With Omnibus GitLab running inside of Kubernetes, you can leverage the bundled
version of Prometheus to collect the required metrics. Once enabled, Prometheus will
version of Prometheus to collect the supported metrics. Once enabled, Prometheus will automatically begin monitoring Kubernetes Nodes and any [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>).
1. Read how to configure the bundled Prometheus server in the
[Administration guide][gitlab-prometheus-k8s-monitor].
......
......@@ -5,13 +5,13 @@ for tracking the evolution of a new idea or the process
of solving a problem.
It allows you, your team, and your collaborators to share
and discuss proposals, before and while implementing them.
and discuss proposals before and while implementing them.
Issues and the GitLab Issue Tracker are available in all
[GitLab Products](https://about.gitlab.com/products/) as
part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
## Use-Cases
## Use cases
Issues can have endless applications. Just to exemplify, these are
some cases for which creating issues are most used:
......@@ -23,7 +23,28 @@ some cases for which creating issues are most used:
- Obtaining support
- Elaborating new code implementations
See also the blog post [Always start a discussion with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/).
See also the blog post "[Always start a discussion with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)".
### Keep private things private
For instance, let's assume you have a public project but want to start a discussion on something
you don't want to be public. With [Confidential Issues](#confidential-issues),
you can discuss private matters among the project members, and still keep
your project public, open to collaboration.
### Streamline collaboration
With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they
would as single assignees), simplifying communication and ownership.
### Consistent collaboration
Create [issue templates](#issue-templates) to make collaboration consistent and
containing all information you need. For example, you can create a template
for feature proposals and another one for bug reports.
## Issue Tracker
......@@ -96,8 +117,8 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature.
[Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards)
are available only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
With [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/), you can also
create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
### Issue's API
......
......@@ -43,7 +43,7 @@ assigned to them if they created the issue themselves.
##### 3.1. Multiple Assignees (EES/EEP)
Issue Weights are only available in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
Multiple Assignees are only available in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams
......@@ -52,9 +52,7 @@ where there is shared ownership of an issue.
In GitLab Enterprise Edition, you can also select multiple assignees
to an issue.
> **Note:**
Multiple Assignees was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1904)
in [GitLab Enterprise Edition 9.2](https://about.gitlab.com/2017/05/22/gitlab-9-2-released/#multiple-assignees-for-issues).
Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html).
#### 4. Milestone
......
......@@ -3,6 +3,59 @@
Merge requests allow you to exchange changes you made to source code and
collaborate with other people on the same project.
## Overview
A Merge Request (**MR**) is the basis of GitLab as a code collaboration
and version control platform.
Is it simple as the name implies: a _request_ to _merge_ one branch into another.
With GitLab merge requests, you can:
- Compare the changes between two [branches](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell#_git_branching)
- [Review and discuss](../../discussions/index.md#discussions) the proposed modifications inline
- Live preview the changes when [Review Apps](../../../ci/review_apps/index.md) is configured for your project
- Build, test, and deploy your code in a per-branch basis with built-in [GitLab CI/CD](../../../ci/README.md)
- Prevent the merge request from being merged before it's ready with [WIP MRs](#work-in-progress-merge-requests)
- View the deployment process through [Pipeline Graphs](../../../ci/pipelines.md#pipeline-graphs)
- [Automatically close the issue(s)](../../project/issues/closing_issues.md#via-merge-request) that originated the implementation proposed in the merge request
- Assign it to any registered user, and change the assignee how many times you need
- Assign a [milestone](../../project/milestones/index.md) and track the development of a broader implementation
- Organize your issues and merge requests consistently throughout the project with [labels](../../project/labels.md)
- Add a time estimation and the time spent with that merge request with [Time Tracking](../../../workflow/time_tracking.html#time-tracking)
- [Resolve merge conflicts from the UI](#resolve-conflicts)
With **[GitLab Enterprise Edition][ee]**, you can also:
- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Enterprise Edition Premium)
- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Enterprise Edition Starter)
- Enable [fast-forward merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/fast_forward_merge.html) (available in GitLab Enterprise Edition Starter)
- [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Enterprise Edition Starter)
- Enable [semi-linear history merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch (available in GitLab Enterprise Edition Starter)
- Analise the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter)
## Use cases
A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter)
1. You build and test your changes with GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Enterprise Edition Starter)
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD
1. Your implementations were successfully shipped to your customer
B. Consider you're a web developer writing a webpage for your company's:
1. You checkout a new branch, and submit a new page through a merge request
1. You gather feedback from your reviewers
1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
1. You request your web designers for their implementation
1. You request the [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your manager (available in GitLab Enterprise Edition Starter)
1. Once approved, your merge request is [squashed and merged](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) (Squash and Merge is available in GitLab Enterprise Edition Starter)
1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
## Authorization for merge requests
There are two main ways to have a merge request flow with GitLab:
......@@ -79,6 +132,16 @@ specific commit page.
You can append `?w=1` while on the diffs page of a merge request to ignore any
whitespace changes.
## Live preview with Review Apps
If you configured [Review Apps](https://about.gitlab.com/features/review-apps/) for your project,
you can preview the changes submitted to a feature-branch through a merge request
in a per-branch basis. No need to checkout the branch, install and preview locally;
all your changes will be available to preview by anyone with the Review Apps link.
[Read more about Review Apps.](../../../ci/review_apps/index.md)
## Tips
Here are some tips that will help you be more efficient with merge requests in
......@@ -167,3 +230,4 @@ git checkout origin/merge-requests/1
```
[protected branches]: ../protected_branches.md
[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition"
......@@ -41,7 +41,7 @@ server up and running for your GitLab instance.
Before we begin, let's understand a few concepts first.
### Static sites
## Static sites
GitLab Pages only supports static websites, meaning,
your output files must be HTML, CSS, and JavaScript only.
......@@ -51,14 +51,14 @@ CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/)
to simplify your code and build the static site for you,
which is highly recommendable and much faster than hardcoding.
#### Further Reading
### Further reading
- Read through this technical overview on [Static versus Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
- Understand [how modern Static Site Generators work](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) and what you can add to your static site
- You can use [any SSG with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
- Fork an [example project](https://gitlab.com/pages) to build your website based upon
### GitLab Pages domain
## GitLab Pages domain
If you set up a GitLab Pages project on GitLab.com,
it will automatically be accessible under a
......@@ -73,9 +73,9 @@ Pages wildcard domain. This guide is valid for any GitLab instance,
you just need to replace Pages wildcard domain on GitLab.com
(`*.gitlab.io`) with your own.
#### Practical examples
### Practical examples
**Project Websites:**
#### Project Websites
- You created a project called `blog` under your username `john`,
therefore your project URL is `https://gitlab.com/john/blog/`.
......@@ -87,16 +87,21 @@ URL is `https://gitlab.com/websites/blog/`. Once you enable
GitLab Pages for this project, the site will live under
`https://websites.gitlab.io/blog/`.
**User and Group Websites:**
#### User and Group Websites
- Under your username, `john`, you created a project called
`john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`.
Once you enable GitLab Pages for your project, your website
will be published under `https://john.gitlab.io`.
- Under your group `websites`, you created a project called
`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project,
`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`.
Once you enable GitLab Pages for your project,
your website will be published under `https://websites.gitlab.io`.
>**Note:**
GitLab Pages [does **not** support subgroups](../../group/subgroups/index.md#limitations).
You can only create the highest level group website.
**General example:**
- On GitLab.com, a project site will always be available under
......
......@@ -398,6 +398,9 @@ don't redirect HTTP to HTTPS.
[rfc]: https://tools.ietf.org/html/rfc2818#section-3.1 "HTTP Over TLS RFC"
GitLab Pages [does **not** support subgroups](../../group/subgroups/index.md#limitations).
You can only create the highest level group website.
## Redirects in GitLab Pages
Since you cannot use any custom server configuration files, like `.htaccess` or
......
......@@ -6,7 +6,7 @@
- [Description templates](../user/project/description_templates.md)
- [Feature branch workflow](workflow.md)
- [GitLab Flow](gitlab_flow.md)
- [Groups](groups.md)
- [Groups](../user/group/index.md)
- Issues - The GitLab Issue Tracker is an advanced and complete tool for
tracking the evolution of a new idea or the process of solving a problem.
- [Confidential issues](../user/project/issues/confidential_issues.md)
......
# GitLab Groups
GitLab groups allow you to group projects into directories and give users access to several projects at once.
When you create a new project in GitLab, the default namespace for the project is the personal namespace associated with your GitLab user.
In this document we will see how to create groups, put projects in groups and manage who can access the projects in a group.
## Creating groups
You can create a group by going to the 'Groups' tab of the GitLab dashboard and clicking the 'New group' button.
![Click the 'New group' button in the 'Groups' tab](groups/new_group_button.png)
Next, enter the path and name (required) and the optional description and group avatar.
![Fill in the path for your new group](groups/new_group_form.png)
When your group has been created you are presented with the group dashboard feed, which will be empty.
![Group dashboard](groups/group_dashboard.png)
You can use the 'New project' button to add a project to the new group.
## Transferring an existing project into a group
You can transfer an existing project into a group you have at least Master access in from the project settings page.
The option to transfer a project is only available if you are the Owner of the project.
First scroll down to the 'Dangerous settings' and click 'Show them to me'.
Now you can pick any of the groups you have at least Master access in as the new namespace for the group.
![Transfer a project to a new namespace](groups/transfer_project.png)
GitLab administrators can use the admin interface to move any project to any namespace if needed.
## Adding users to a group
One of the benefits of putting multiple projects in one group is that you can give a user to access to all projects in the group with one action.
Suppose we have a group with two projects.
![Group with two projects](groups/group_with_two_projects.png)
On the 'Group Members' page we can now add a new user Barry to the group.
![Add user Barry to the group](groups/add_member_to_group.png)
Now because Barry is a 'Developer' member of the 'Open Source' group, he automatically gets 'Developer' access to all projects in the 'Open Source' group.
![Barry has 'Developer' access to GitLab CI](groups/project_members_via_group.png)
If necessary, you can increase the access level of an individual user for a specific project, by adding them as a Member to the project.
![Barry effectively has 'Master' access to GitLab CI now](groups/override_access_level.png)
## Requesting access to a group
As a group owner you can enable or disable non members to request access to
your group. Go to the group settings and click on **Allow users to request access**.
As a user, you can request to be a member of a group. Go to the group you'd
like to be a member of, and click the **Request Access** button on the right
side of your screen.
![Request access button](groups/request_access_button.png)
---
Group owners & masters will be notified of your request and will be able to approve or
decline it on the members page.
![Manage access requests](groups/access_requests_management.png)
---
If you change your mind before your request is approved, just click the
**Withdraw Access Request** button.
![Withdraw access request button](groups/withdraw_access_request_button.png)
## Managing group memberships via LDAP
In GitLab Enterprise Edition it is possible to manage GitLab group memberships using LDAP groups.
See [the GitLab Enterprise Edition documentation](http://docs.gitlab.com/ee/integration/ldap.html) for more information.
## Allowing only admins to create groups
By default, any GitLab user can create new groups.
This ability can be disabled for individual users from the admin panel.
It is also possible to configure GitLab so that new users default to not being able to create groups:
```
# For omnibus-gitlab, put the following in /etc/gitlab/gitlab.rb
gitlab_rails['gitlab_default_can_create_group'] = false
# For installations from source, uncomment the 'default_can_create_group'
# line in /home/git/gitlab/config/gitlab.yml
```
This document was moved to [another location](../user/group/index.md).
......@@ -5,7 +5,7 @@ to a project with a single action.
## Groups as collections of users
Groups are used primarily to [create collections of projects](groups.md), but you can also
Groups are used primarily to [create collections of projects](../user/group/index.md), but you can also
take advantage of the fact that groups define collections of _users_, namely the group
members.
......
......@@ -150,7 +150,7 @@ module API
#
# begin
# repository = wiki? ? project.wiki.repository : project.repository
# Gitlab::GitalyClient::Notifications.new(repository.raw_repository).post_receive
# Gitlab::GitalyClient::NotificationService.new(repository.raw_repository).post_receive
# rescue GRPC::Unavailable => e
# render_api_error!(e, 500)
# end
......
......@@ -8,7 +8,12 @@ require_dependency 'declarative_policy/step'
require_dependency 'declarative_policy/base'
require 'thread'
module DeclarativePolicy
CLASS_CACHE_MUTEX = Mutex.new
CLASS_CACHE_IVAR = :@__DeclarativePolicy_CLASS_CACHE
class << self
def policy_for(user, subject, opts = {})
cache = opts[:cache] || {}
......@@ -23,7 +28,36 @@ module DeclarativePolicy
subject = find_delegate(subject)
subject.class.ancestors.each do |klass|
class_for_class(subject.class)
end
private
# This method is heavily cached because there are a lot of anonymous
# modules in play in a typical rails app, and #name performs quite
# slowly for anonymous classes and modules.
#
# See https://bugs.ruby-lang.org/issues/11119
#
# if the above bug is resolved, this caching could likely be removed.
def class_for_class(subject_class)
unless subject_class.instance_variable_defined?(CLASS_CACHE_IVAR)
CLASS_CACHE_MUTEX.synchronize do
# re-check in case of a race
break if subject_class.instance_variable_defined?(CLASS_CACHE_IVAR)
policy_class = compute_class_for_class(subject_class)
subject_class.instance_variable_set(CLASS_CACHE_IVAR, policy_class)
end
end
policy_class = subject_class.instance_variable_get(CLASS_CACHE_IVAR)
raise "no policy for #{subject.class.name}" if policy_class.nil?
policy_class
end
def compute_class_for_class(subject_class)
subject_class.ancestors.each do |klass|
next unless klass.name
begin
......@@ -37,12 +71,8 @@ module DeclarativePolicy
nil
end
end
raise "no policy for #{subject.class.name}"
end
private
def find_delegate(subject)
seen = Set.new
......
......@@ -21,11 +21,14 @@ module DeclarativePolicy
private
def id_for(obj)
if obj.respond_to?(:id) && obj.id
obj.id.to_s
else
"##{obj.object_id}"
end
id =
begin
obj.id
rescue NoMethodError
nil
end
id || "##{obj.object_id}"
end
end
end
......
......@@ -82,13 +82,14 @@ module DeclarativePolicy
# depending on the scope, we may cache only by the user or only by
# the subject, resulting in sharing across different policy objects.
def cache_key
case @condition.scope
when :normal then "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}"
when :user then "/dp/condition/#{@condition.key}/#{user_key}"
when :subject then "/dp/condition/#{@condition.key}/#{subject_key}"
when :global then "/dp/condition/#{@condition.key}"
else raise 'invalid scope'
end
@cache_key ||=
case @condition.scope
when :normal then "/dp/condition/#{@condition.key}/#{user_key},#{subject_key}"
when :user then "/dp/condition/#{@condition.key}/#{user_key}"
when :subject then "/dp/condition/#{@condition.key}/#{subject_key}"
when :global then "/dp/condition/#{@condition.key}"
else raise 'invalid scope'
end
end
def user_key
......
module Gitlab
module BackgroundMigration
def self.queue
@queue ||= BackgroundMigrationWorker.sidekiq_options['queue']
end
# Begins stealing jobs from the background migrations queue, blocking the
# caller until all jobs have been completed.
#
# When a migration raises a StandardError is is going to be retries up to
# three times, for example, to recover from a deadlock.
#
# When Exception is being raised, it enqueues the migration again, and
# re-raises the exception.
#
# steal_class - The name of the class for which to steal jobs.
def self.steal(steal_class)
queue = Sidekiq::Queue
.new(BackgroundMigrationWorker.sidekiq_options['queue'])
enqueued = Sidekiq::Queue.new(self.queue)
scheduled = Sidekiq::ScheduledSet.new
queue.each do |job|
migration_class, migration_args = job.args
[scheduled, enqueued].each do |queue|
queue.each do |job|
migration_class, migration_args = job.args
next unless migration_class == steal_class
next unless job.queue == self.queue
next unless migration_class == steal_class
perform(migration_class, migration_args)
begin
perform(migration_class, migration_args, retries: 3) if job.delete
rescue Exception # rubocop:disable Lint/RescueException
BackgroundMigrationWorker # enqueue this migration again
.perform_async(migration_class, migration_args)
job.delete
raise
end
end
end
end
##
# Performs a background migration.
#
# class_name - The name of the background migration class as defined in the
# Gitlab::BackgroundMigration namespace.
#
......
module Gitlab
module Cache
# This module provides a simple way to cache values in RequestStore,
# and the cache key would be based on the class name, method name,
# optionally customized instance level values, optionally customized
# method level values, and optional method arguments.
#
# A simple example:
#
# class UserAccess
# extend Gitlab::Cache::RequestCache
#
# request_cache_key do
# [user&.id, project&.id]
# end
#
# request_cache def can_push_to_branch?(ref)
# # ...
# end
# end
#
# This way, the result of `can_push_to_branch?` would be cached in
# `RequestStore.store` based on the cache key. If RequestStore is not
# currently active, then it would be stored in a hash saved in an
# instance variable, so the cache logic would be the same.
# Here's another example using customized method level values:
#
# class Commit
# extend Gitlab::Cache::RequestCache
#
# def author
# User.find_by_any_email(author_email.downcase)
# end
# request_cache(:author) { author_email.downcase }
# end
#
# So that we could have different strategies for different methods
#
module RequestCache
def self.extended(klass)
return if klass < self
extension = Module.new
klass.const_set(:RequestCacheExtension, extension)
klass.prepend(extension)
end
def request_cache_key(&block)
if block_given?
@request_cache_key = block
else
@request_cache_key
end
end
def request_cache(method_name, &method_key_block)
const_get(:RequestCacheExtension).module_eval do
cache_key_method_name = "#{method_name}_cache_key"
define_method(method_name) do |*args|
store =
if RequestStore.active?
RequestStore.store
else
ivar_name = # ! and ? cannot be used as ivar name
"@cache_#{method_name.to_s.tr('!?', "\u2605\u2606")}"
instance_variable_get(ivar_name) ||
instance_variable_set(ivar_name, {})
end
key = __send__(cache_key_method_name, args)
store.fetch(key) { store[key] = super(*args) }
end
define_method(cache_key_method_name) do |args|
klass = self.class
instance_key = instance_exec(&klass.request_cache_key) if
klass.request_cache_key
method_key = instance_exec(&method_key_block) if method_key_block
[klass.name, method_name, *instance_key, *method_key, *args]
.join(':')
end
private cache_key_method_name
end
end
end
end
end
......@@ -140,6 +140,8 @@ module Gitlab
return add_foreign_key(source, target,
column: column,
on_delete: on_delete)
else
on_delete = 'SET NULL' if on_delete == :nullify
end
disable_statement_timeout
......@@ -155,7 +157,7 @@ module Gitlab
ADD CONSTRAINT #{key_name}
FOREIGN KEY (#{column})
REFERENCES #{target} (id)
#{on_delete ? "ON DELETE #{on_delete}" : ''}
#{on_delete ? "ON DELETE #{on_delete.upcase}" : ''}
NOT VALID;
EOF
......
......@@ -29,7 +29,7 @@ module Gitlab
path = path.sub(/\A\/*/, '')
path = '/' if path.empty?
name = File.basename(path)
entry = Gitlab::GitalyClient::Commit.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE)
entry = Gitlab::GitalyClient::CommitService.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE)
return unless entry
case entry.type
......@@ -87,10 +87,10 @@ module Gitlab
def raw(repository, sha)
Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled|
if is_enabled
Gitlab::GitalyClient::Blob.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
else
blob = repository.lookup(sha)
new(
id: blob.oid,
size: blob.size,
......@@ -182,7 +182,7 @@ module Gitlab
Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled|
@data = begin
if is_enabled
Gitlab::GitalyClient::Blob.new(repository).get_blob(oid: id, limit: -1).data
Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: id, limit: -1).data
else
repository.lookup(id).content
end
......
......@@ -3,39 +3,8 @@
module Gitlab
module Git
class Branch < Ref
def initialize(repository, name, target)
if target.is_a?(Gitaly::FindLocalBranchResponse)
target = target_from_gitaly_local_branches_response(target)
end
super(repository, name, target)
end
def target_from_gitaly_local_branches_response(response)
# Git messages have no encoding enforcements. However, in the UI we only
# handle UTF-8, so basically we cross our fingers that the message force
# encoded to UTF-8 is readable.
message = response.commit_subject.dup.force_encoding('UTF-8')
# NOTE: For ease of parsing in Gitaly, we have only the subject of
# the commit and not the full message. This is ok, since all the
# code that uses `local_branches` only cares at most about the
# commit message.
# TODO: Once gitaly "takes over" Rugged consider separating the
# subject from the message to make it clearer when there's one
# available but not the other.
hash = {
id: response.commit_id,
message: message,
authored_date: Time.at(response.commit_author.date.seconds),
author_name: response.commit_author.name,
author_email: response.commit_author.email,
committed_date: Time.at(response.commit_committer.date.seconds),
committer_name: response.commit_committer.name,
committer_email: response.commit_committer.email
}
Gitlab::Git::Commit.decorate(hash)
def initialize(repository, name, target, target_commit)
super(repository, name, target, target_commit)
end
end
end
......
......@@ -33,10 +33,9 @@ module Gitlab
object
end
def initialize(repository, name, target)
encode! name
@name = name.gsub(/\Arefs\/(tags|heads)\//, '')
@dereferenced_target = Gitlab::Git::Commit.find(repository, target)
def initialize(repository, name, target, derefenced_target)
@name = Gitlab::Git.ref_name(name)
@dereferenced_target = derefenced_target
@target = if target.respond_to?(:oid)
target.oid
elsif target.respond_to?(:name)
......
......@@ -80,16 +80,10 @@ module Gitlab
end
# Returns an Array of Branches
def branches(filter: nil, sort_by: nil)
branches = rugged.branches.each(filter).map do |rugged_ref|
begin
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target)
rescue Rugged::ReferenceError
# Omit invalid branch
end
end.compact
sort_branches(branches, sort_by)
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/389
def branches(sort_by: nil)
branches_filter(sort_by: sort_by)
end
def reload_rugged
......@@ -107,7 +101,10 @@ module Gitlab
reload_rugged if force_reload
rugged_ref = rugged.branches[name]
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target) if rugged_ref
if rugged_ref
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
end
end
def local_branches(sort_by: nil)
......@@ -115,7 +112,7 @@ module Gitlab
if is_enabled
gitaly_ref_client.local_branches(sort_by: sort_by)
else
branches(filter: :local, sort_by: sort_by)
branches_filter(filter: :local, sort_by: sort_by)
end
end
end
......@@ -162,6 +159,8 @@ module Gitlab
end
# Returns an Array of Tags
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390
def tags
rugged.references.each("refs/tags/*").map do |ref|
message = nil
......@@ -174,7 +173,8 @@ module Gitlab
end
end
Gitlab::Git::Tag.new(self, ref.name, ref.target, message)
target_commit = Gitlab::Git::Commit.find(self, ref.target)
Gitlab::Git::Tag.new(self, ref.name, ref.target, target_commit, message)
end.sort_by(&:name)
end
......@@ -204,13 +204,6 @@ module Gitlab
branch_names + tag_names
end
# Deprecated. Will be removed in 5.2
def heads
rugged.references.each("refs/heads/*").map do |head|
Gitlab::Git::Ref.new(self, head.name, head.target)
end.sort_by(&:name)
end
def has_commits?
!empty?
end
......@@ -297,28 +290,6 @@ module Gitlab
(size.to_f / 1024).round(2)
end
# Returns an array of BlobSnippets for files at the specified +ref+ that
# contain the +query+ string.
def search_files(query, ref = nil)
greps = []
ref ||= root_ref
populated_index(ref).each do |entry|
# Discard submodules
next if submodule?(entry)
blob = Gitlab::Git::Blob.raw(self, entry[:oid])
# Skip binary files
next if blob.data.encoding == Encoding::ASCII_8BIT
blob.load_all_data!(self)
greps += build_greps(blob.data, query, ref, entry[:path])
end
greps
end
# Use the Rugged Walker API to build an array of commits.
#
# Usage.
......@@ -707,7 +678,8 @@ module Gitlab
# create_branch("other-feature", "master")
def create_branch(ref, start_point = "HEAD")
rugged_ref = rugged.branches.create(ref, start_point)
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target)
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
rescue Rugged::ReferenceError => e
raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ /'refs\/heads\/#{ref}'/
raise InvalidRef.new("Invalid reference #{start_point}")
......@@ -837,6 +809,20 @@ module Gitlab
private
# Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'.
def branches_filter(filter: nil, sort_by: nil)
branches = rugged.branches.each(filter).map do |rugged_ref|
begin
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
rescue Rugged::ReferenceError
# Omit invalid branch
end
end.compact
sort_branches(branches, sort_by)
end
def raw_log(options)
default_options = {
limit: 10,
......@@ -1091,73 +1077,6 @@ module Gitlab
index
end
# Return an array of BlobSnippets for lines in +file_contents+ that match
# +query+
def build_greps(file_contents, query, ref, filename)
# The file_contents string is potentially huge so we make sure to loop
# through it one line at a time. This gives Ruby the chance to GC lines
# we are not interested in.
#
# We need to do a little extra work because we are not looking for just
# the lines that matches the query, but also for the context
# (surrounding lines). We will use Enumerable#each_cons to efficiently
# loop through the lines while keeping surrounding lines on hand.
#
# First, we turn "foo\nbar\nbaz" into
# [
# [nil, -3], [nil, -2], [nil, -1],
# ['foo', 0], ['bar', 1], ['baz', 3],
# [nil, 4], [nil, 5], [nil, 6]
# ]
lines_with_index = Enumerator.new do |yielder|
# Yield fake 'before' lines for the first line of file_contents
(-SEARCH_CONTEXT_LINES..-1).each do |i|
yielder.yield [nil, i]
end
# Yield the actual file contents
count = 0
file_contents.each_line do |line|
line.chomp!
yielder.yield [line, count]
count += 1
end
# Yield fake 'after' lines for the last line of file_contents
(count + 1..count + SEARCH_CONTEXT_LINES).each do |i|
yielder.yield [nil, i]
end
end
greps = []
# Loop through consecutive blocks of lines with indexes
lines_with_index.each_cons(2 * SEARCH_CONTEXT_LINES + 1) do |line_block|
# Get the 'middle' line and index from the block
line, _ = line_block[SEARCH_CONTEXT_LINES]
next unless line && line.match(/#{Regexp.escape(query)}/i)
# Yay, 'line' contains a match!
# Get an array with just the context lines (no indexes)
match_with_context = line_block.map(&:first)
# Remove 'nil' lines in case we are close to the first or last line
match_with_context.compact!
# Get the line number (1-indexed) of the first context line
first_context_line_number = line_block[0][1] + 1
greps << Gitlab::Git::BlobSnippet.new(
ref,
match_with_context,
first_context_line_number,
filename
)
end
greps
end
# Return the Rugged patches for the diff between +from+ and +to+.
def diff_patches(from, to, options = {}, *paths)
options ||= {}
......@@ -1187,11 +1106,11 @@ module Gitlab
end
def gitaly_ref_client
@gitaly_ref_client ||= Gitlab::GitalyClient::Ref.new(self)
@gitaly_ref_client ||= Gitlab::GitalyClient::RefService.new(self)
end
def gitaly_commit_client
@gitaly_commit_client ||= Gitlab::GitalyClient::Commit.new(self)
@gitaly_commit_client ||= Gitlab::GitalyClient::CommitService.new(self)
end
def gitaly_migrate(method, &block)
......
......@@ -5,8 +5,8 @@ module Gitlab
class Tag < Ref
attr_reader :object_sha
def initialize(repository, name, target, message = nil)
super(repository, name, target)
def initialize(repository, name, target, target_commit, message = nil)
super(repository, name, target, target_commit)
@message = message
end
......
module Gitlab
module GitalyClient
class Blob
class BlobService
def initialize(repository)
@gitaly_repo = repository.gitaly_repository
end
def get_blob(oid:, limit:)
request = Gitaly::GetBlobRequest.new(
repository: @gitaly_repo,
......
module Gitlab
module GitalyClient
class Commit
class CommitService
# The ID of empty tree.
# See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
......@@ -17,20 +17,20 @@ module Gitlab
child_id: child_id
)
GitalyClient.call(@repository.storage, :commit, :commit_is_ancestor, request).value
GitalyClient.call(@repository.storage, :commit_service, :commit_is_ancestor, request).value
end
def diff_from_parent(commit, options = {})
request_params = commit_diff_request_params(commit, options)
request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
request = Gitaly::CommitDiffRequest.new(request_params)
response = GitalyClient.call(@repository.storage, :diff, :commit_diff, request)
response = GitalyClient.call(@repository.storage, :diff_service, :commit_diff, request)
Gitlab::Git::DiffCollection.new(GitalyClient::DiffStitcher.new(response), options)
end
def commit_deltas(commit)
request = Gitaly::CommitDeltaRequest.new(commit_diff_request_params(commit))
response = GitalyClient.call(@repository.storage, :diff, :commit_delta, request)
response = GitalyClient.call(@repository.storage, :diff_service, :commit_delta, request)
response.flat_map do |msg|
msg.deltas.map { |d| Gitlab::Git::Diff.new(d) }
end
......@@ -44,7 +44,7 @@ module Gitlab
limit: limit.to_i
)
response = GitalyClient.call(@repository.storage, :commit, :tree_entry, request)
response = GitalyClient.call(@repository.storage, :commit_service, :tree_entry, request)
entry = response.first
return unless entry.oid.present?
......
module Gitlab
module GitalyClient
class Notifications
class NotificationService
# 'repository' is a Gitlab::Git::Repository
def initialize(repository)
@gitaly_repo = repository.gitaly_repository
......@@ -10,7 +10,7 @@ module Gitlab
def post_receive
GitalyClient.call(
@storage,
:notifications,
:notification_service,
:post_receive,
Gitaly::PostReceiveRequest.new(repository: @gitaly_repo)
)
......
module Gitlab
module GitalyClient
class Ref
class RefService
include Gitlab::EncodingHelper
# 'repository' is a Gitlab::Git::Repository
......@@ -12,19 +12,19 @@ module Gitlab
def default_branch_name
request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :ref, :find_default_branch_name, request)
response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request)
Gitlab::Git.branch_name(response.name)
end
def branch_names
request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :ref, :find_all_branch_names, request)
response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request)
consume_refs_response(response) { |name| Gitlab::Git.branch_name(name) }
end
def tag_names
request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
response = GitalyClient.call(@storage, :ref, :find_all_tag_names, request)
response = GitalyClient.call(@storage, :ref_service, :find_all_tag_names, request)
consume_refs_response(response) { |name| Gitlab::Git.tag_name(name) }
end
......@@ -34,7 +34,7 @@ module Gitlab
commit_id: commit_id,
prefix: ref_prefix
)
encode!(GitalyClient.call(@storage, :ref, :find_ref_name, request).name.dup)
encode!(GitalyClient.call(@storage, :ref_service, :find_ref_name, request).name.dup)
end
def count_tag_names
......@@ -48,7 +48,7 @@ module Gitlab
def local_branches(sort_by: nil)
request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo)
request.sort_by = sort_by_param(sort_by) if sort_by
response = GitalyClient.call(@storage, :ref, :find_local_branches, request)
response = GitalyClient.call(@storage, :ref_service, :find_local_branches, request)
consume_branches_response(response)
end
......@@ -72,11 +72,39 @@ module Gitlab
Gitlab::Git::Branch.new(
@repository,
encode!(gitaly_branch.name.dup),
gitaly_branch.commit_id
gitaly_branch.commit_id,
commit_from_local_branches_response(gitaly_branch)
)
end
end
end
def commit_from_local_branches_response(response)
# Git messages have no encoding enforcements. However, in the UI we only
# handle UTF-8, so basically we cross our fingers that the message force
# encoded to UTF-8 is readable.
message = response.commit_subject.dup.force_encoding('UTF-8')
# NOTE: For ease of parsing in Gitaly, we have only the subject of
# the commit and not the full message. This is ok, since all the
# code that uses `local_branches` only cares at most about the
# commit message.
# TODO: Once gitaly "takes over" Rugged consider separating the
# subject from the message to make it clearer when there's one
# available but not the other.
hash = {
id: response.commit_id,
message: message,
authored_date: Time.at(response.commit_author.date.seconds),
author_name: response.commit_author.name,
author_email: response.commit_author.email,
committed_date: Time.at(response.commit_committer.date.seconds),
committer_name: response.commit_committer.name,
committer_email: response.commit_committer.email
}
Gitlab::Git::Commit.decorate(hash)
end
end
end
end
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册