Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/persistent-import-data
This commit is contained in:
commit
0b2469a4e5
|
@ -1,4 +0,0 @@
|
|||
# Prefer single quotes
|
||||
StringLiterals:
|
||||
EnforcedStyle: single_quotes
|
||||
Enabled: true
|
|
@ -1,8 +0,0 @@
|
|||
stage:
|
||||
before:
|
||||
- cp config/gitlab.teatro.yml config/gitlab.yml
|
||||
- mkdir /apps/gitlab-satellites
|
||||
- mkdir /apps/repositories
|
||||
|
||||
database:
|
||||
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
|
24
CHANGELOG
24
CHANGELOG
|
@ -1,11 +1,14 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.10.0 (unreleased)
|
||||
- Expose {should,force}_remove_source_branch (Ben Boeckel)
|
||||
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
|
||||
- Replace Haml with Hamlit to make view rendering faster. !3666
|
||||
- Expire the branch cache after `git gc` runs
|
||||
- Refactor repository paths handling to allow multiple git mount points
|
||||
- Optimize system note visibility checking by memoizing the visible reference count !5070
|
||||
- Add Application Setting to configure default Repository Path for new projects
|
||||
- Delete award emoji when deleting a user
|
||||
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
|
||||
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
|
||||
- Align flash messages with left side of page content !4959 (winniehell)
|
||||
|
@ -30,7 +33,9 @@ v 8.10.0 (unreleased)
|
|||
- Exclude email check from the standard health check
|
||||
- Updated layout for Projects, Groups, Users on Admin area !4424
|
||||
- Fix changing issue state columns in milestone view
|
||||
- Update health_check gem to version 2.1.0
|
||||
- Add notification settings dropdown for groups
|
||||
- Render inline diffs for multiple changed lines following eachother
|
||||
- Wildcards for protected branches. !4665
|
||||
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
|
||||
- API: Todos !3188 (Robert Schilling)
|
||||
|
@ -38,6 +43,7 @@ v 8.10.0 (unreleased)
|
|||
- Add "Enabled Git access protocols" to Application Settings
|
||||
- Diffs will create button/diff form on demand no on server side
|
||||
- Reduce size of HTML used by diff comment forms
|
||||
- Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
|
||||
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
|
||||
- Only show New Snippet button to users that can create snippets.
|
||||
- PipelinesFinder uses git cache data
|
||||
|
@ -46,14 +52,17 @@ v 8.10.0 (unreleased)
|
|||
- Collapse large diffs by default (!4990)
|
||||
- Check for conflicts with existing Project's wiki path when creating a new project.
|
||||
- Show last push widget in upstream after push to fork
|
||||
- Cache todos pending/done dashboard query counts.
|
||||
- Don't instantiate a git tree on Projects show default view
|
||||
- Bump Rinku to 2.0.0
|
||||
- Remove unused front-end variable -> default_issues_tracker
|
||||
- ObjectRenderer retrieve renderer content using Rails.cache.read_multi
|
||||
- Better caching of git calls on ProjectsController#show.
|
||||
- Avoid to retrieve MR closes_issues as much as possible.
|
||||
- Add API endpoint for a group issues !4520 (mahcsig)
|
||||
- Add Bugzilla integration !4930 (iamtjg)
|
||||
- Instrument Rinku usage
|
||||
- Be explicit to define merge request discussion variables
|
||||
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
|
||||
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
|
||||
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
|
||||
|
@ -67,12 +76,25 @@ v 8.10.0 (unreleased)
|
|||
- Allow '?', or '&' for label names
|
||||
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
|
||||
- Add date when user joined the team on the member page
|
||||
- Fix 404 redirect after validation fails importing a GitLab project
|
||||
- Fix 404 redirect after validation fails importing a GitLab project
|
||||
- Fix 404 redirect after validation fails importing a GitLab project
|
||||
- Added setting to set new users by default as external !4545 (Dravere)
|
||||
- Add min value for project limit field on user's form !3622 (jastkand)
|
||||
- Reset project pushes_since_gc when we enqueue the git gc call
|
||||
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
|
||||
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
|
||||
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
|
||||
- Fix GitHub client requests when rate limit is disabled
|
||||
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
|
||||
- Redesign Builds and Pipelines pages
|
||||
- Change status color and icon for running builds
|
||||
|
||||
v 8.9.6
|
||||
- Fix importing of events under notes for GitLab projects. !5154
|
||||
- Fix log statements in import/export. !5129
|
||||
- Fix commit avatar alignment in compare view. !5128
|
||||
- Fix broken migration in MySQL. !5005
|
||||
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213
|
||||
|
||||
v 8.9.7 (unreleased)
|
||||
- Fix import_data wrongly saved as a result of an invalid import_url
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.7.7
|
||||
0.7.8
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0'
|
|||
gem 'paranoia', '~> 2.0'
|
||||
|
||||
# Health check
|
||||
gem 'health_check', '~> 1.5.1'
|
||||
gem 'health_check', '~> 2.1.0'
|
||||
|
||||
# System information
|
||||
gem 'vmstat', '~> 2.1.0'
|
||||
|
|
|
@ -322,8 +322,8 @@ GEM
|
|||
thor
|
||||
tilt
|
||||
hashie (3.4.3)
|
||||
health_check (1.5.1)
|
||||
rails (>= 2.3.0)
|
||||
health_check (2.1.0)
|
||||
rails (>= 4.0)
|
||||
hipchat (1.5.2)
|
||||
httparty
|
||||
mimemagic
|
||||
|
@ -870,7 +870,7 @@ DEPENDENCIES
|
|||
grape (~> 0.13.0)
|
||||
grape-entity (~> 0.4.2)
|
||||
hamlit (~> 2.5)
|
||||
health_check (~> 1.5.1)
|
||||
health_check (~> 2.1.0)
|
||||
hipchat (~> 1.5.0)
|
||||
html-pipeline (~> 1.11.0)
|
||||
httparty (~> 0.13.3)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
class @Diff
|
||||
UNFOLD_COUNT = 20
|
||||
constructor: ->
|
||||
$('.files .diff-file').singleFileDiff()
|
||||
@filesCommentButton = $('.files .diff-file').filesCommentButton()
|
||||
|
||||
$(document).off('click', '.js-unfold')
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
$ ->
|
||||
$(".protected-branches-list :checkbox").change (e) ->
|
||||
name = $(this).attr("name")
|
||||
if name == "developers_can_push"
|
||||
if name == "developers_can_push" || name == "developers_can_merge"
|
||||
id = $(this).val()
|
||||
checked = $(this).is(":checked")
|
||||
can_push = $(this).is(":checked")
|
||||
url = $(this).data("url")
|
||||
$.ajax
|
||||
type: "PUT"
|
||||
type: "PATCH"
|
||||
url: url
|
||||
dataType: "json"
|
||||
data:
|
||||
id: id
|
||||
protected_branch:
|
||||
developers_can_push: checked
|
||||
"#{name}": can_push
|
||||
|
||||
success: ->
|
||||
row = $(e.target)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
}
|
||||
|
||||
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
|
||||
&.s20 { width: 20px; height: 20px; margin-right: 7px; }
|
||||
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
|
||||
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
|
||||
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
.toggle-nav-collapse,
|
||||
.pin-nav-btn {
|
||||
color: $color-light;
|
||||
background: $color;
|
||||
|
||||
&:hover {
|
||||
color: $white-light;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
height: 100%;
|
||||
overflow: hidden;
|
||||
transition: width $sidebar-transition-duration;
|
||||
@include box-shadow(2px 0 16px 0 #bbb);
|
||||
@include box-shadow(2px 0 16px 0 $black-transparent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@
|
|||
}
|
||||
|
||||
a {
|
||||
padding: 7px 15px 7px 12px;
|
||||
padding: 7px 16px;
|
||||
font-size: $gl-font-size;
|
||||
line-height: 24px;
|
||||
display: block;
|
||||
|
@ -108,7 +108,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.toggle-nav-collapse {
|
||||
.sidebar-action-buttons {
|
||||
width: $sidebar_width;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -117,12 +117,37 @@
|
|||
padding: 5px 0;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
|
||||
.toggle-nav-collapse {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.pin-nav-btn {
|
||||
right: 0;
|
||||
display: none;
|
||||
|
||||
@media (min-width: $sidebar-breakpoint) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.fa {
|
||||
transition: transform .15s;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
.fa {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-header-btn {
|
||||
padding: 10px 5px;
|
||||
padding: 10px 16px;
|
||||
color: inherit;
|
||||
transition-duration: .3s;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
|
@ -131,30 +156,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.pin-nav-btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 50px;
|
||||
width: $sidebar_width;
|
||||
line-height: 30px;
|
||||
|
||||
@media (min-width: $sidebar-breakpoint) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.fa {
|
||||
transition: transform .15s;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
.fa {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-sidebar-collapsed {
|
||||
padding-left: 0;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ $focus-border-color: #3aabf0;
|
|||
$table-border-color: #f0f0f0;
|
||||
$background-color: #fafafa;
|
||||
$dark-background-color: #f7f7f7;
|
||||
$table-text-gray: #8f8f8f;
|
||||
|
||||
/*
|
||||
* Text
|
||||
|
|
|
@ -83,14 +83,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
table.builds {
|
||||
.build-link {
|
||||
a {
|
||||
color: $gl-dark-link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.build-trace {
|
||||
background: $ci-output-bg;
|
||||
color: $ci-text-color;
|
||||
|
|
|
@ -162,9 +162,15 @@
|
|||
}
|
||||
|
||||
.filtered-labels {
|
||||
font-size: 0;
|
||||
padding: 12px 16px;
|
||||
|
||||
.label-row {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,11 +73,14 @@
|
|||
color: #888;
|
||||
}
|
||||
|
||||
&.ci-pending,
|
||||
&.ci-running {
|
||||
&.ci-pending {
|
||||
color: $gl-warning;
|
||||
}
|
||||
|
||||
&.ci-running {
|
||||
color: $blue-normal;
|
||||
}
|
||||
|
||||
&.ci-failed,
|
||||
&.ci-error {
|
||||
color: $gl-danger;
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
.pipelines {
|
||||
.stage {
|
||||
max-width: 100px;
|
||||
max-width: 80px;
|
||||
width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.duration, .finished_at {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -22,3 +19,136 @@
|
|||
margin: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-list {
|
||||
|
||||
&.pipelines,
|
||||
&.builds-content-list {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.table.builds {
|
||||
min-width: 1100px;
|
||||
|
||||
tr {
|
||||
th {
|
||||
padding: 16px;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.branch-commit {
|
||||
|
||||
.branch-name {
|
||||
margin-left: 8px;
|
||||
font-weight: bold;
|
||||
max-width: 180px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
vertical-align: top;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin: 0 6px;
|
||||
height: 14px;
|
||||
width: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.commit-id {
|
||||
color: $gl-link-color;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.commit-title {
|
||||
margin-top: 4px;
|
||||
max-width: 320px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.label-container {
|
||||
|
||||
.label {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.duration,
|
||||
.finished-at {
|
||||
color: $table-text-gray;
|
||||
margin: 4px 0;
|
||||
|
||||
.fa {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 12px;
|
||||
width: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.fa,
|
||||
svg {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.pipeline-actions {
|
||||
|
||||
.btn {
|
||||
margin: 0;
|
||||
color: $table-text-gray;
|
||||
}
|
||||
|
||||
.dropdown-toggle,
|
||||
.dropdown-menu {
|
||||
color: $table-text-gray;
|
||||
|
||||
.fa {
|
||||
color: $table-text-gray;
|
||||
margin-right: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
color: $white-light;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
&.open {
|
||||
.btn-default {
|
||||
background-color: $white-normal;
|
||||
border-color: $border-white-normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.build-link {
|
||||
|
||||
a {
|
||||
color: $gl-dark-link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group.open .dropdown-toggle {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -340,23 +340,30 @@ a.deploy-project-label {
|
|||
|
||||
.project-import {
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.import-buttons {
|
||||
padding-left: 0;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.btn {
|
||||
margin-right: 10px;
|
||||
padding: 8px 12px;
|
||||
margin: 0 10px 10px 0;
|
||||
padding: 8px;
|
||||
}
|
||||
&> div {
|
||||
margin-bottom: 14px;
|
||||
|
||||
> div {
|
||||
padding-left: 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
|
||||
.btn {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,11 +32,15 @@
|
|||
border-color: $gl-gray;
|
||||
}
|
||||
|
||||
&.ci-pending,
|
||||
&.ci-running {
|
||||
&.ci-pending {
|
||||
color: $gl-warning;
|
||||
border-color: $gl-warning;
|
||||
}
|
||||
|
||||
&.ci-running {
|
||||
color: $blue-normal;
|
||||
border-color: $blue-normal;
|
||||
}
|
||||
}
|
||||
|
||||
.ci-status-icon-success {
|
||||
|
@ -45,10 +49,12 @@
|
|||
.ci-status-icon-failed {
|
||||
color: $gl-danger;
|
||||
}
|
||||
.ci-status-icon-running,
|
||||
.ci-status-icon-pending {
|
||||
color: $gl-warning;
|
||||
}
|
||||
.ci-status-icon-running {
|
||||
color: $blue-normal;
|
||||
}
|
||||
.ci-status-icon-canceled,
|
||||
.ci-status-icon-disabled,
|
||||
.ci-status-icon-not-found,
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
class Dashboard::TodosController < Dashboard::ApplicationController
|
||||
include TodosHelper
|
||||
|
||||
before_action :find_todos, only: [:index, :destroy_all]
|
||||
|
||||
def index
|
||||
|
@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
|||
respond_to do |format|
|
||||
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
|
||||
format.js { head :ok }
|
||||
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
|
||||
format.json { render json: todos_counts }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
|||
respond_to do |format|
|
||||
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
|
||||
format.js { head :ok }
|
||||
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
|
||||
format.json { render json: todos_counts }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
|||
def find_todos
|
||||
@todos ||= TodosFinder.new(current_user, params).execute
|
||||
end
|
||||
|
||||
def todos_counts
|
||||
{
|
||||
count: TodosFinder.new(current_user, state: :pending).execute.count,
|
||||
done_count: TodosFinder.new(current_user, state: :done).execute.count
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||
# Only allow properly saved users to login.
|
||||
if @user.persisted? && @user.valid?
|
||||
log_audit_event(@user, with: oauth['provider'])
|
||||
sign_in_and_redirect(@user)
|
||||
if @user.two_factor_enabled?
|
||||
prompt_for_two_factor(@user)
|
||||
else
|
||||
sign_in_and_redirect(@user)
|
||||
end
|
||||
else
|
||||
error_message = @user.errors.full_messages.to_sentence
|
||||
|
||||
|
|
|
@ -119,6 +119,10 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
||||
end
|
||||
end
|
||||
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
@conflict = true
|
||||
render :edit
|
||||
end
|
||||
|
||||
def referenced_merge_requests
|
||||
|
@ -216,7 +220,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
def issue_params
|
||||
params.require(:issue).permit(
|
||||
:title, :assignee_id, :position, :description, :confidential,
|
||||
:milestone_id, :due_date, :state_event, :task_num, label_ids: []
|
||||
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.html { define_discussion_vars }
|
||||
|
||||
format.json do
|
||||
render json: @merge_request
|
||||
|
@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
@merge_request_diff = @merge_request.merge_request_diff
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.html { define_discussion_vars }
|
||||
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
|
||||
end
|
||||
end
|
||||
|
@ -108,7 +108,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def commits
|
||||
respond_to do |format|
|
||||
format.html { render 'show' }
|
||||
format.html do
|
||||
define_discussion_vars
|
||||
|
||||
render 'show'
|
||||
end
|
||||
format.json do
|
||||
# Get commits from repository
|
||||
# or from cache if already merged
|
||||
|
@ -123,7 +127,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def builds
|
||||
respond_to do |format|
|
||||
format.html { render 'show' }
|
||||
format.html do
|
||||
define_discussion_vars
|
||||
|
||||
render 'show'
|
||||
end
|
||||
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
|
||||
end
|
||||
end
|
||||
|
@ -188,6 +196,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
else
|
||||
render "edit"
|
||||
end
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
@conflict = true
|
||||
render :edit
|
||||
end
|
||||
|
||||
def remove_wip
|
||||
|
@ -353,14 +364,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
@merge_request.unlock_mr
|
||||
@merge_request.close
|
||||
end
|
||||
|
||||
if request.format == :html || action_name == 'show'
|
||||
define_show_html_vars
|
||||
end
|
||||
end
|
||||
|
||||
# Discussion tab data is only required on html requests
|
||||
def define_show_html_vars
|
||||
# Discussion tab data is rendered on html responses of actions
|
||||
# :show, :diff, :commits, :builds. but not when request the data through AJAX
|
||||
def define_discussion_vars
|
||||
# Build a note object for comment form
|
||||
@note = @project.notes.new(noteable: @noteable)
|
||||
|
||||
|
@ -419,7 +427,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
:title, :assignee_id, :source_project_id, :source_branch,
|
||||
:target_project_id, :target_branch, :milestone_id,
|
||||
:state_event, :description, :task_num, :force_remove_source_branch,
|
||||
label_ids: []
|
||||
:lock_version, label_ids: []
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -50,6 +50,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def protected_branch_params
|
||||
params.require(:protected_branch).permit(:name, :developers_can_push)
|
||||
params.require(:protected_branch).permit(:name, :developers_can_push, :developers_can_merge)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController
|
|||
todo = TodoService.new.mark_todo(issuable, current_user)
|
||||
|
||||
render json: {
|
||||
count: current_user.todos_pending_count,
|
||||
count: TodosFinder.new(current_user, state: :pending).execute.count,
|
||||
delete_path: dashboard_todo_path(todo)
|
||||
}
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# action_id: integer
|
||||
# author_id: integer
|
||||
# project_id; integer
|
||||
# state: 'pending' or 'done'
|
||||
# state: 'pending' (default) or 'done'
|
||||
# type: 'Issue' or 'MergeRequest'
|
||||
#
|
||||
|
||||
|
@ -37,7 +37,7 @@ class TodosFinder
|
|||
private
|
||||
|
||||
def action_id?
|
||||
action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
|
||||
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
|
||||
end
|
||||
|
||||
def action_id
|
||||
|
|
|
@ -31,7 +31,7 @@ module AppearancesHelper
|
|||
end
|
||||
end
|
||||
|
||||
def navbar_icon(icon_name, size: 16)
|
||||
def custom_icon(icon_name, size: 16)
|
||||
render "shared/icons/#{icon_name}.svg", size: size
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ module BranchesHelper
|
|||
def can_push_branch?(project, branch_name)
|
||||
return false unless project.repository.branch_exists?(branch_name)
|
||||
|
||||
::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(branch_name)
|
||||
::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
|
||||
end
|
||||
|
||||
def project_branches
|
||||
|
|
|
@ -29,8 +29,10 @@ module CiStatusHelper
|
|||
'check'
|
||||
when 'failed'
|
||||
'close'
|
||||
when 'running', 'pending'
|
||||
when 'pending'
|
||||
'clock-o'
|
||||
when 'running'
|
||||
'spinner'
|
||||
else
|
||||
'circle'
|
||||
end
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module TodosHelper
|
||||
def todos_pending_count
|
||||
TodosFinder.new(current_user, state: :pending).execute.count
|
||||
@todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
|
||||
end
|
||||
|
||||
def todos_done_count
|
||||
TodosFinder.new(current_user, state: :done).execute.count
|
||||
@todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
|
||||
end
|
||||
|
||||
def todo_action_name(todo)
|
||||
|
@ -13,6 +13,7 @@ module TodosHelper
|
|||
when Todo::MENTIONED then 'mentioned you on'
|
||||
when Todo::BUILD_FAILED then 'The build failed for your'
|
||||
when Todo::MARKED then 'added a todo for'
|
||||
when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -87,6 +87,12 @@ module Issuable
|
|||
User.find(assignee_id_was).update_cache_counts if assignee_id_was
|
||||
assignee.update_cache_counts if assignee
|
||||
end
|
||||
|
||||
# We want to use optimistic lock for cases when only title or description are involved
|
||||
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
|
||||
def locking_enabled?
|
||||
title_changed? || description_changed?
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
|
|
@ -552,7 +552,13 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def can_be_merged_by?(user)
|
||||
::Gitlab::GitAccess.new(user, project, 'web').can_push_to_branch?(target_branch)
|
||||
access = ::Gitlab::UserAccess.new(user, project: project)
|
||||
access.can_push_to_branch?(target_branch) || access.can_merge_to_branch?(target_branch)
|
||||
end
|
||||
|
||||
def can_be_merged_via_command_line_by?(user)
|
||||
access = ::Gitlab::UserAccess.new(user, project: project)
|
||||
access.can_push_to_branch?(target_branch)
|
||||
end
|
||||
|
||||
def mergeable_ci_state?
|
||||
|
|
|
@ -836,6 +836,10 @@ class Project < ActiveRecord::Base
|
|||
protected_branches.matching(branch_name).any?(&:developers_can_push)
|
||||
end
|
||||
|
||||
def developers_can_merge_to_protected_branch?(branch_name)
|
||||
protected_branches.matching(branch_name).any?(&:developers_can_merge)
|
||||
end
|
||||
|
||||
def forked?
|
||||
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
|
||||
end
|
||||
|
|
|
@ -769,9 +769,9 @@ class Repository
|
|||
end
|
||||
end
|
||||
|
||||
def merge(user, source_sha, target_branch, options = {})
|
||||
our_commit = rugged.branches[target_branch].target
|
||||
their_commit = rugged.lookup(source_sha)
|
||||
def merge(user, merge_request, options = {})
|
||||
our_commit = rugged.branches[merge_request.target_branch].target
|
||||
their_commit = rugged.lookup(merge_request.diff_head_sha)
|
||||
|
||||
raise "Invalid merge target" if our_commit.nil?
|
||||
raise "Invalid merge source" if their_commit.nil?
|
||||
|
@ -779,14 +779,16 @@ class Repository
|
|||
merge_index = rugged.merge_commits(our_commit, their_commit)
|
||||
return false if merge_index.conflicts?
|
||||
|
||||
commit_with_hooks(user, target_branch) do |ref|
|
||||
commit_with_hooks(user, merge_request.target_branch) do |tmp_ref|
|
||||
actual_options = options.merge(
|
||||
parents: [our_commit, their_commit],
|
||||
tree: merge_index.write_tree(rugged),
|
||||
update_ref: ref
|
||||
update_ref: tmp_ref
|
||||
)
|
||||
|
||||
Rugged::Commit.create(rugged, actual_options)
|
||||
commit_id = Rugged::Commit.create(rugged, actual_options)
|
||||
merge_request.update(in_progress_merge_commit_sha: commit_id)
|
||||
commit_id
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
class Todo < ActiveRecord::Base
|
||||
ASSIGNED = 1
|
||||
MENTIONED = 2
|
||||
BUILD_FAILED = 3
|
||||
MARKED = 4
|
||||
ASSIGNED = 1
|
||||
MENTIONED = 2
|
||||
BUILD_FAILED = 3
|
||||
MARKED = 4
|
||||
APPROVAL_REQUIRED = 5 # This is an EE-only feature
|
||||
|
||||
ACTION_NAMES = {
|
||||
ASSIGNED => :assigned,
|
||||
MENTIONED => :mentioned,
|
||||
BUILD_FAILED => :build_failed,
|
||||
MARKED => :marked
|
||||
MARKED => :marked,
|
||||
APPROVAL_REQUIRED => :approval_required
|
||||
}
|
||||
|
||||
belongs_to :author, class_name: "User"
|
||||
|
|
|
@ -87,7 +87,7 @@ class User < ActiveRecord::Base
|
|||
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
|
||||
has_many :todos, dependent: :destroy
|
||||
has_many :notification_settings, dependent: :destroy
|
||||
has_many :award_emoji, as: :awardable, dependent: :destroy
|
||||
has_many :award_emoji, dependent: :destroy
|
||||
|
||||
#
|
||||
# Validations
|
||||
|
|
|
@ -23,7 +23,7 @@ module Commits
|
|||
private
|
||||
|
||||
def check_push_permissions
|
||||
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
|
||||
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
|
||||
|
||||
unless allowed
|
||||
raise ValidationError.new('You are not allowed to push into this branch')
|
||||
|
@ -31,7 +31,7 @@ module Commits
|
|||
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
def create_target_branch(new_branch)
|
||||
# Temporary branch exists and contains the change commit
|
||||
return success if repository.find_branch(new_branch)
|
||||
|
|
|
@ -42,7 +42,7 @@ module Files
|
|||
end
|
||||
|
||||
def validate
|
||||
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
|
||||
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
|
||||
|
||||
unless allowed
|
||||
raise_error("You are not allowed to push into this branch")
|
||||
|
|
|
@ -89,7 +89,8 @@ class GitPushService < BaseService
|
|||
# Set protection on the default branch if configured
|
||||
if current_application_settings.default_branch_protection != PROTECTION_NONE
|
||||
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
|
||||
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push })
|
||||
developers_can_merge = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_MERGE ? true : false
|
||||
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ module MergeRequests
|
|||
committer: committer
|
||||
}
|
||||
|
||||
commit_id = repository.merge(current_user, merge_request.diff_head_sha, merge_request.target_branch, options)
|
||||
commit_id = repository.merge(current_user, merge_request, options)
|
||||
merge_request.update(merge_commit_sha: commit_id)
|
||||
rescue GitHooksService::PreReceiveError => e
|
||||
merge_request.update(merge_error: e.message)
|
||||
|
@ -43,6 +43,8 @@ module MergeRequests
|
|||
merge_request.update(merge_error: "Something went wrong during merge")
|
||||
Rails.logger.error(e.message)
|
||||
false
|
||||
ensure
|
||||
merge_request.update(in_progress_merge_commit_sha: nil)
|
||||
end
|
||||
|
||||
def after_merge
|
||||
|
|
|
@ -48,7 +48,7 @@ module MergeRequests
|
|||
end
|
||||
|
||||
def force_push?
|
||||
Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev)
|
||||
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
|
||||
end
|
||||
|
||||
# Refresh merge request diff if we push to source or target branch of merge request
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#
|
||||
module Projects
|
||||
class HousekeepingService < BaseService
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
LEASE_TIMEOUT = 3600
|
||||
|
||||
class LeaseTaken < StandardError
|
||||
|
@ -24,11 +22,7 @@ module Projects
|
|||
def execute
|
||||
raise LeaseTaken unless try_obtain_lease
|
||||
|
||||
GitlabShellOneShotWorker.perform_async(:gc, @project.repository_storage_path, @project.path_with_namespace)
|
||||
ensure
|
||||
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
|
||||
update_pushes_since_gc(0)
|
||||
end
|
||||
execute_gitlab_shell_gc
|
||||
end
|
||||
|
||||
def needed?
|
||||
|
@ -36,19 +30,27 @@ module Projects
|
|||
end
|
||||
|
||||
def increment!
|
||||
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
|
||||
update_pushes_since_gc(@project.pushes_since_gc + 1)
|
||||
if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
|
||||
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
|
||||
update_pushes_since_gc(@project.pushes_since_gc + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_pushes_since_gc(new_value)
|
||||
if Gitlab::ExclusiveLease.new("project_housekeeping:update_pushes_since_gc:#{project.id}", timeout: 60).try_obtain
|
||||
@project.update_column(:pushes_since_gc, new_value)
|
||||
def execute_gitlab_shell_gc
|
||||
GitGarbageCollectWorker.perform_async(@project.id)
|
||||
ensure
|
||||
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
|
||||
update_pushes_since_gc(0)
|
||||
end
|
||||
end
|
||||
|
||||
def update_pushes_since_gc(new_value)
|
||||
@project.update_column(:pushes_since_gc, new_value)
|
||||
end
|
||||
|
||||
def try_obtain_lease
|
||||
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
|
||||
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
|
||||
|
|
|
@ -1,31 +1,41 @@
|
|||
- project = build.project
|
||||
%tr.build
|
||||
%tr.build.commit
|
||||
%td.status
|
||||
= ci_status_with_icon(build.status)
|
||||
|
||||
%td.build-link
|
||||
- if can?(current_user, :read_build, build.project)
|
||||
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
||||
%strong Build ##{build.id}
|
||||
- else
|
||||
%strong Build ##{build.id}
|
||||
%td
|
||||
.branch-commit
|
||||
- if can?(current_user, :read_build, build.project)
|
||||
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
||||
%span.build-link ##{build.id}
|
||||
- else
|
||||
%span.build-link ##{build.id}
|
||||
|
||||
- if build.stuck?
|
||||
%i.fa.fa-warning.text-warning
|
||||
- if build.stuck?
|
||||
%i.fa.fa-warning.text-warning
|
||||
|
||||
- if build.ref
|
||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
|
||||
- else
|
||||
.light none
|
||||
= custom_icon("icon_commit")
|
||||
|
||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
|
||||
|
||||
.label-container
|
||||
- if build.tags.any?
|
||||
- build.tags.each do |tag|
|
||||
%span.label.label-primary
|
||||
= tag
|
||||
- if build.try(:trigger_request)
|
||||
%span.label.label-info triggered
|
||||
- if build.try(:allow_failure)
|
||||
%span.label.label-danger allowed to fail
|
||||
|
||||
%td
|
||||
- if project
|
||||
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
|
||||
|
||||
%td
|
||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
|
||||
|
||||
%td
|
||||
- if build.ref
|
||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
|
||||
- else
|
||||
.light none
|
||||
|
||||
%td
|
||||
- if build.try(:runner)
|
||||
= runner_link(build.runner)
|
||||
|
@ -36,22 +46,15 @@
|
|||
#{build.stage} / #{build.name}
|
||||
|
||||
%td
|
||||
- if build.tags.any?
|
||||
- build.tags.each do |tag|
|
||||
%span.label.label-primary
|
||||
= tag
|
||||
- if build.try(:trigger_request)
|
||||
%span.label.label-info triggered
|
||||
- if build.try(:allow_failure)
|
||||
%span.label.label-danger allowed to fail
|
||||
|
||||
%td.duration
|
||||
- if build.duration
|
||||
#{duration_in_words(build.finished_at, build.started_at)}
|
||||
%p.duration
|
||||
= custom_icon("icon_timer")
|
||||
= duration_in_numbers(build.finished_at, build.started_at)
|
||||
|
||||
%td.timestamp
|
||||
- if build.finished_at
|
||||
%span #{time_ago_with_tooltip(build.finished_at)}
|
||||
%p.finished-at
|
||||
= icon("calendar")
|
||||
%span #{time_ago_with_tooltip(build.finished_at)}
|
||||
|
||||
- if defined?(coverage) && coverage
|
||||
%td.coverage
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
.row-content-block.second-block
|
||||
#{(@scope || 'all').capitalize} builds
|
||||
|
||||
%ul.content-list
|
||||
%ul.content-list.builds-content-list
|
||||
- if @builds.blank?
|
||||
%li
|
||||
.nothing-here-block No builds to show
|
||||
|
@ -37,15 +37,11 @@
|
|||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Build ID
|
||||
%th Project
|
||||
%th Commit
|
||||
%th Ref
|
||||
%th Project
|
||||
%th Runner
|
||||
%th Name
|
||||
%th Tags
|
||||
%th Duration
|
||||
%th Finished at
|
||||
%th
|
||||
%th
|
||||
|
||||
- @builds.each do |build|
|
||||
|
|
|
@ -9,14 +9,14 @@
|
|||
%span
|
||||
To do
|
||||
%span.badge
|
||||
= todos_pending_count
|
||||
= number_with_delimiter(todos_pending_count)
|
||||
- todo_done_active = ('active' if params[:state] == 'done')
|
||||
%li{class: "todos-done #{todo_done_active}"}
|
||||
= link_to todos_filter_path(state: 'done') do
|
||||
%span
|
||||
Done
|
||||
%span.badge
|
||||
= todos_done_count
|
||||
= number_with_delimiter(todos_done_count)
|
||||
|
||||
.nav-controls
|
||||
- if @todos.any?(&:pending?)
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
.nav-controls
|
||||
- if can?(current_user, :admin_milestones, @group)
|
||||
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
|
||||
= icon('plus')
|
||||
New Milestone
|
||||
|
||||
.row-content-block
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
- if can? current_user, :admin_group, @group
|
||||
.controls
|
||||
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
|
||||
= icon('plus')
|
||||
New Project
|
||||
%ul.well-list
|
||||
- @projects.each do |project|
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
.cover-block.groups-cover-block
|
||||
%div{ class: container_class }
|
||||
= link_to group_icon(@group), target: '_blank' do
|
||||
= image_tag group_icon(@group), class: "avatar group-avatar s70"
|
||||
= image_tag group_icon(@group), class: "avatar group-avatar s70"
|
||||
.group-info
|
||||
.cover-title
|
||||
%h1
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
= link_to '#', class: 'nav-header-btn text-center toggle-nav-collapse', title: "Open/Close" do
|
||||
%span.sr-only Toggle navigation
|
||||
= icon('bars')
|
|
@ -1,6 +1,13 @@
|
|||
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
|
||||
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
|
||||
= render partial: 'layouts/collapse_button'
|
||||
.sidebar-action-buttons
|
||||
= link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do
|
||||
%span.sr-only Toggle navigation
|
||||
= icon('bars')
|
||||
= link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
|
||||
%span.sr-only Toggle navigation pinning
|
||||
= icon('thumb-tack')
|
||||
|
||||
- if defined?(sidebar) && sidebar
|
||||
= render "layouts/nav/#{sidebar}"
|
||||
- elsif current_user
|
||||
|
@ -8,9 +15,6 @@
|
|||
- else
|
||||
= render 'layouts/nav/explore'
|
||||
|
||||
= link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
|
||||
%span.sr-only Toggle navigation pinning
|
||||
= icon('thumb-tack')
|
||||
- if defined?(nav) && nav
|
||||
.layout-nav
|
||||
.container-fluid
|
||||
|
|
|
@ -1,64 +1,44 @@
|
|||
%ul.nav.nav-sidebar
|
||||
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
|
||||
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
|
||||
.icon-container
|
||||
= navbar_icon('project')
|
||||
%span
|
||||
Projects
|
||||
= nav_link(controller: :todos) do
|
||||
= link_to dashboard_todos_path, title: 'Todos' do
|
||||
.icon-container
|
||||
= icon('bell fw')
|
||||
%span
|
||||
Todos
|
||||
%span.count= number_with_delimiter(todos_pending_count)
|
||||
= nav_link(path: 'dashboard#activity') do
|
||||
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
|
||||
.icon-container
|
||||
= navbar_icon('activity')
|
||||
%span
|
||||
Activity
|
||||
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
|
||||
= link_to dashboard_groups_path, title: 'Groups' do
|
||||
.icon-container
|
||||
= navbar_icon('group')
|
||||
%span
|
||||
Groups
|
||||
= nav_link(controller: 'dashboard/milestones') do
|
||||
= link_to dashboard_milestones_path, title: 'Milestones' do
|
||||
.icon-container
|
||||
= navbar_icon('milestones')
|
||||
%span
|
||||
Milestones
|
||||
= nav_link(path: 'dashboard#issues') do
|
||||
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
|
||||
.icon-container
|
||||
= navbar_icon('issues')
|
||||
%span
|
||||
Issues
|
||||
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
|
||||
= nav_link(path: 'dashboard#merge_requests') do
|
||||
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
|
||||
.icon-container
|
||||
= navbar_icon('mr')
|
||||
%span
|
||||
Merge Requests
|
||||
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
|
||||
= nav_link(controller: :snippets) do
|
||||
= link_to dashboard_snippets_path, title: 'Snippets' do
|
||||
.icon-container
|
||||
= icon('clipboard fw')
|
||||
%span
|
||||
Snippets
|
||||
= nav_link(controller: :help) do
|
||||
= link_to help_path, title: 'Help' do
|
||||
.icon-container
|
||||
= icon('question-circle fw')
|
||||
%span
|
||||
Help
|
||||
= nav_link(html_options: {class: profile_tab_class}) do
|
||||
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
|
||||
.icon-container
|
||||
= icon('user fw')
|
||||
%span
|
||||
Profile Settings
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
%span
|
||||
Applications
|
||||
= nav_link(controller: :personal_access_tokens) do
|
||||
= link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
|
||||
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
|
||||
%span
|
||||
Personal Access Tokens
|
||||
Access Tokens
|
||||
= nav_link(controller: :emails) do
|
||||
= link_to profile_emails_path, title: 'Emails' do
|
||||
%span
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
%span CI Lint
|
||||
|
||||
%ul.content-list
|
||||
%ul.content-list.builds-content-list
|
||||
- if @builds.blank?
|
||||
%li
|
||||
.nothing-here-block No builds to show
|
||||
|
@ -46,14 +46,10 @@
|
|||
%thead
|
||||
%tr
|
||||
%th Status
|
||||
%th Build ID
|
||||
%th Commit
|
||||
%th Ref
|
||||
%th Stage
|
||||
%th Name
|
||||
%th Tags
|
||||
%th Duration
|
||||
%th Finished at
|
||||
%th
|
||||
- if @project.build_coverage_enabled?
|
||||
%th Coverage
|
||||
%th
|
||||
|
|
|
@ -1,32 +1,45 @@
|
|||
%tr.build
|
||||
%tr.build.commit
|
||||
%td.status
|
||||
- if can?(current_user, :read_build, build)
|
||||
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
|
||||
- else
|
||||
= ci_status_with_icon(build.status)
|
||||
|
||||
%td.build-link
|
||||
- if can?(current_user, :read_build, build)
|
||||
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
||||
%strong ##{build.id}
|
||||
- else
|
||||
%strong ##{build.id}
|
||||
|
||||
- if build.stuck?
|
||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
|
||||
- if defined?(retried) && retried
|
||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
|
||||
|
||||
- if defined?(commit_sha) && commit_sha
|
||||
%td
|
||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
|
||||
|
||||
- if defined?(ref) && ref
|
||||
%td
|
||||
- if build.ref
|
||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
|
||||
%td
|
||||
.branch-commit
|
||||
- if can?(current_user, :read_build, build)
|
||||
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
||||
%span ##{build.id}
|
||||
- else
|
||||
.light none
|
||||
%span ##{build.id}
|
||||
|
||||
- if build.stuck?
|
||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
|
||||
- if defined?(retried) && retried
|
||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
|
||||
|
||||
- if defined?(ref) && ref
|
||||
- if build.ref
|
||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
|
||||
- else
|
||||
.light none
|
||||
= custom_icon("icon_commit")
|
||||
|
||||
- if defined?(commit_sha) && commit_sha
|
||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
|
||||
|
||||
.label-container
|
||||
- if build.tags.any?
|
||||
- build.tags.each do |tag|
|
||||
%span.label.label-primary
|
||||
= tag
|
||||
- if build.try(:trigger_request)
|
||||
%span.label.label-info triggered
|
||||
- if build.try(:allow_failure)
|
||||
%span.label.label-danger allowed to fail
|
||||
- if defined?(retried) && retried
|
||||
%span.label.label-warning retried
|
||||
|
||||
|
||||
- if defined?(runner) && runner
|
||||
%td
|
||||
|
@ -43,25 +56,14 @@
|
|||
= build.name
|
||||
|
||||
%td
|
||||
.label-container
|
||||
- if build.tags.any?
|
||||
- build.tags.each do |tag|
|
||||
%span.label.label-primary
|
||||
= tag
|
||||
- if build.try(:trigger_request)
|
||||
%span.label.label-info triggered
|
||||
- if build.try(:allow_failure)
|
||||
%span.label.label-danger allowed to fail
|
||||
- if defined?(retried) && retried
|
||||
%span.label.label-warning retried
|
||||
|
||||
%td.duration
|
||||
- if build.duration
|
||||
#{duration_in_words(build.finished_at, build.started_at)}
|
||||
|
||||
%td.timestamp
|
||||
%p.duration
|
||||
= custom_icon("icon_timer")
|
||||
= duration_in_numbers(build.finished_at, build.started_at)
|
||||
- if build.finished_at
|
||||
%span #{time_ago_with_tooltip(build.finished_at)}
|
||||
%p.finished-at
|
||||
= icon("calendar")
|
||||
%span #{time_ago_with_tooltip(build.finished_at)}
|
||||
|
||||
- if defined?(coverage) && coverage
|
||||
%td.coverage
|
||||
|
@ -79,4 +81,4 @@
|
|||
= icon('remove', class: 'cred')
|
||||
- elsif defined?(allow_retry) && allow_retry && build.retryable?
|
||||
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
|
||||
= icon('refresh')
|
||||
= icon('repeat')
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
- status = pipeline.status
|
||||
%tr.commit
|
||||
%td.commit-link
|
||||
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: "ci-status ci-#{status}" do
|
||||
= ci_icon_for_status(status)
|
||||
%strong ##{pipeline.id}
|
||||
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
|
||||
= ci_status_with_icon(status)
|
||||
|
||||
|
||||
%td
|
||||
%div.branch-commit
|
||||
.branch-commit
|
||||
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
|
||||
%span ##{pipeline.id}
|
||||
- if pipeline.ref
|
||||
= link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
|
||||
·
|
||||
= link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace branch-name"
|
||||
= custom_icon("icon_commit")
|
||||
= link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
|
||||
|
||||
- if pipeline.tag?
|
||||
%span.label.label-primary tag
|
||||
- elsif pipeline.latest?
|
||||
|
@ -25,6 +26,7 @@
|
|||
|
||||
%p.commit-title
|
||||
- if commit = pipeline.commit
|
||||
= commit_author_avatar(commit, size: 20)
|
||||
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
|
||||
- else
|
||||
Cant find HEAD commit for this branch
|
||||
|
@ -45,22 +47,37 @@
|
|||
%td
|
||||
- if pipeline.started_at && pipeline.finished_at
|
||||
%p.duration
|
||||
= custom_icon("icon_timer")
|
||||
= duration_in_numbers(pipeline.finished_at, pipeline.started_at)
|
||||
- if pipeline.finished_at
|
||||
%p.finished-at
|
||||
= icon("calendar")
|
||||
#{time_ago_with_tooltip(pipeline.finished_at)}
|
||||
|
||||
%td
|
||||
%td.pipeline-actions
|
||||
.controls.hidden-xs.pull-right
|
||||
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
|
||||
- if artifacts.present?
|
||||
.dropdown.inline.build-artifacts
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
= icon('download')
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
- artifacts.each do |build|
|
||||
.btn-group.inline
|
||||
.btn-group
|
||||
%a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
= icon("play")
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
|
||||
= icon("download")
|
||||
%span Download '#{build.name}' artifacts
|
||||
= link_to '#' do
|
||||
= icon("play")
|
||||
%span Deploy to production
|
||||
.btn-group
|
||||
%a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
= icon("download")
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
- artifacts.each do |build|
|
||||
%li
|
||||
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
|
||||
= icon("download")
|
||||
%span Download '#{build.name}' artifacts
|
||||
|
||||
- if can?(current_user, :update_pipeline, @project)
|
||||
- if pipeline.retryable?
|
||||
|
|
|
@ -42,9 +42,7 @@
|
|||
%th Status
|
||||
%th Build ID
|
||||
%th Name
|
||||
%th Tags
|
||||
%th Duration
|
||||
%th Finished at
|
||||
%th
|
||||
- if pipeline.project.build_coverage_enabled?
|
||||
%th Coverage
|
||||
%th
|
||||
|
|
|
@ -50,10 +50,12 @@
|
|||
|
||||
%td.duration
|
||||
- if generic_commit_status.duration
|
||||
= icon("clock-o")
|
||||
#{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
|
||||
|
||||
%td.timestamp
|
||||
- if generic_commit_status.finished_at
|
||||
= icon("calendar")
|
||||
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
|
||||
|
||||
- if defined?(coverage) && coverage
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
Code, test, and deploy together
|
||||
.blank-state
|
||||
.blank-state-icon
|
||||
= navbar_icon("issues", size: 50)
|
||||
= custom_icon("issues", size: 50)
|
||||
%h3.blank-state-title
|
||||
You don't have any issues right now.
|
||||
%p.blank-state-text
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
%p
|
||||
Please resolve these conflicts or
|
||||
- if @merge_request.can_be_merged_by?(current_user)
|
||||
- if @merge_request.can_be_merged_via_command_line_by?(current_user)
|
||||
#{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}.
|
||||
- else
|
||||
ask someone with write access to this repository to merge this request manually.
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
.nav-controls
|
||||
- if can? current_user, :create_pipeline, @project
|
||||
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
|
||||
New pipeline
|
||||
Run pipeline
|
||||
|
||||
- unless @repository.gitlab_ci_yml
|
||||
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
||||
|
@ -45,13 +45,13 @@
|
|||
.table-holder
|
||||
%table.table.builds
|
||||
%tbody
|
||||
%th ID
|
||||
%th Status
|
||||
%th Commit
|
||||
- stages.each do |stage|
|
||||
%th.stage
|
||||
%span.has-tooltip{ title: "#{stage.titleize}" }
|
||||
= stage.titleize
|
||||
%th Duration
|
||||
%th
|
||||
%th
|
||||
= render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
.table-responsive
|
||||
%table.table.protected-branches-list
|
||||
%colgroup
|
||||
%col{ width: "20%" }
|
||||
%col{ width: "30%" }
|
||||
%col{ width: "25%" }
|
||||
%col{ width: "25%" }
|
||||
|
@ -18,6 +19,7 @@
|
|||
%th Protected Branch
|
||||
%th Commit
|
||||
%th Developers Can Push
|
||||
%th Developers Can Merge
|
||||
- if can_admin_project
|
||||
%th
|
||||
%tbody
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
(branch was removed from repository)
|
||||
%td
|
||||
= check_box_tag("developers_can_push", protected_branch.id, protected_branch.developers_can_push, data: { url: url })
|
||||
%td
|
||||
= check_box_tag("developers_can_merge", protected_branch.id, protected_branch.developers_can_merge, data: { url: url })
|
||||
- if can_admin_project
|
||||
%td
|
||||
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
|
||||
|
|
|
@ -36,6 +36,14 @@
|
|||
= f.label :developers_can_push, "Developers can push", class: "label-light append-bottom-0"
|
||||
%p.light.append-bottom-0
|
||||
Allow developers to push to this branch
|
||||
|
||||
.form-group
|
||||
= f.check_box :developers_can_merge, class: "pull-left"
|
||||
.prepend-left-20
|
||||
= f.label :developers_can_merge, "Developers can merge", class: "label-light append-bottom-0"
|
||||
%p.light.append-bottom-0
|
||||
Allow developers to accept merge requests to this branch
|
||||
= f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
|
||||
|
||||
%hr
|
||||
= render "branches_list"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- page_title "Snippets"
|
||||
|
||||
.row-content-block.top-block
|
||||
.sub-header-block
|
||||
.pull-right
|
||||
- if can?(current_user, :create_project_snippet, @project)
|
||||
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
|
||||
<path fill="#8F8F8F" fill-rule="evenodd" d="M28.7769836,18 C27.8675252,13.9920226 24.2831748,11 20,11 C15.7168252,11 12.1324748,13.9920226 11.2230164,18 L4.0085302,18 C2.90195036,18 2,18.8954305 2,20 C2,21.1122704 2.8992496,22 4.0085302,22 L11.2230164,22 C12.1324748,26.0079774 15.7168252,29 20,29 C24.2831748,29 27.8675252,26.0079774 28.7769836,22 L35.9914698,22 C37.0980496,22 38,21.1045695 38,20 C38,18.8877296 37.1007504,18 35.9914698,18 L28.7769836,18 L28.7769836,18 Z M20,25 C22.7614237,25 25,22.7614237 25,20 C25,17.2385763 22.7614237,15 20,15 C17.2385763,15 15,17.2385763 15,20 C15,22.7614237 17.2385763,25 20,25 L20,25 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 726 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg>
|
After Width: | Height: | Size: 748 B |
|
@ -1,5 +1,12 @@
|
|||
= form_errors(issuable)
|
||||
|
||||
- if @conflict
|
||||
.alert.alert-danger
|
||||
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
|
||||
Please check out
|
||||
= link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank"
|
||||
and make sure your changes will not unintentionally remove theirs
|
||||
|
||||
.form-group
|
||||
= f.label :title, class: 'control-label'
|
||||
.col-sm-10
|
||||
|
@ -149,3 +156,5 @@
|
|||
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
|
||||
method: :delete, class: 'btn btn-danger btn-grouped'
|
||||
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
|
||||
|
||||
= f.hidden_field :lock_version
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
%span.pull-right.tab-issues-buttons
|
||||
- if project && can?(current_user, :create_issue, project)
|
||||
= link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn btn-grouped", title: "New Issue" do
|
||||
%i.fa.fa-plus
|
||||
New Issue
|
||||
= link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped"
|
||||
%span.pull-right.tab-merge-requests-buttons.hidden
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
class GitGarbageCollectWorker
|
||||
include Sidekiq::Worker
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
sidekiq_options queue: :gitlab_shell, retry: false
|
||||
|
||||
def perform(project_id)
|
||||
project = Project.find(project_id)
|
||||
|
||||
gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace)
|
||||
# Expire the branch cache in case garbage collection caused a ref lookup to fail
|
||||
project.repository.after_create_branch
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
class GitlabShellOneShotWorker
|
||||
include Sidekiq::Worker
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
sidekiq_options queue: :gitlab_shell, retry: false
|
||||
|
||||
def perform(action, *arg)
|
||||
gitlab_shell.send(action, *arg)
|
||||
end
|
||||
end
|
|
@ -1,87 +0,0 @@
|
|||
|
||||
production: &base
|
||||
gitlab:
|
||||
host: localhost
|
||||
port: 80
|
||||
https: false
|
||||
|
||||
user: root
|
||||
|
||||
email_from: example@example.com
|
||||
|
||||
support_email: support@example.com
|
||||
|
||||
default_projects_features:
|
||||
issues: true
|
||||
merge_requests: true
|
||||
wiki: true
|
||||
snippets: false
|
||||
visibility_level: "private" # can be "private" | "internal" | "public"
|
||||
|
||||
issues_tracker:
|
||||
|
||||
gravatar:
|
||||
enabled: true # Use user avatar image from Gravatar.com (default: true)
|
||||
|
||||
ldap:
|
||||
enabled: false
|
||||
host: '_your_ldap_server'
|
||||
port: 636
|
||||
uid: 'sAMAccountName'
|
||||
method: 'ssl' # "tls" or "ssl" or "plain"
|
||||
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
|
||||
password: '_the_password_of_the_bind_user'
|
||||
allow_username_or_email_login: true
|
||||
|
||||
base: ''
|
||||
|
||||
user_filter: ''
|
||||
|
||||
omniauth:
|
||||
enabled: false
|
||||
|
||||
satellites:
|
||||
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
|
||||
path: /apps/gitlab-satellites/
|
||||
|
||||
backup:
|
||||
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
|
||||
|
||||
repositories:
|
||||
storages: # REPO PATHS MUST NOT BE A SYMLINK!!!
|
||||
default: /apps/repositories/
|
||||
|
||||
gitlab_shell:
|
||||
path: /apps/gitlab-shell/
|
||||
|
||||
hooks_path: /apps/gitlab-shell/hooks/
|
||||
|
||||
upload_pack: true
|
||||
receive_pack: true
|
||||
|
||||
git:
|
||||
bin_path: /usr/bin/git
|
||||
max_size: 5242880 # 5.megabytes
|
||||
timeout: 10
|
||||
|
||||
extra:
|
||||
|
||||
development:
|
||||
<<: *base
|
||||
|
||||
test:
|
||||
<<: *base
|
||||
gravatar:
|
||||
enabled: true
|
||||
gitlab:
|
||||
host: localhost
|
||||
port: 80
|
||||
issues_tracker:
|
||||
redmine:
|
||||
title: "Redmine"
|
||||
project_url: "http://redmine/projects/:issues_tracker_id"
|
||||
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
|
||||
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
|
||||
|
||||
staging:
|
||||
<<: *base
|
|
@ -1,16 +1,3 @@
|
|||
# Email forcibly included in the standard checks, but the email health check
|
||||
# doesn't support the full range of SMTP options, which can result in failures
|
||||
# for valid SMTP configurations.
|
||||
# Overwrite the HealthCheck's detection of whether email is configured
|
||||
# in order to avoid the email check during standard checks
|
||||
module HealthCheck
|
||||
class Utils
|
||||
def self.mailer_configured?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
HealthCheck.setup do |config|
|
||||
config.standard_checks = ['database', 'migrations', 'cache']
|
||||
config.full_checks = ['database', 'migrations', 'cache']
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class AddDevelopersCanMergeToProtectedBranches < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_column_with_default :protected_branches, :developers_can_merge, :boolean, default: false, allow_null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddColumnInProgressMergeCommitShaToMergeRequests < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :merge_requests, :in_progress_merge_commit_sha, :string
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddLockToIssuables < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_column_with_default :issues, :lock_version, :integer, default: 0
|
||||
add_column_with_default :merge_requests, :lock_version, :integer, default: 0
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :issues, :lock_version
|
||||
remove_column :merge_requests, :lock_version
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class RemoveAwardEmojisWithNoUser < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# When using the methods "add_concurrent_index" or "add_column_with_default"
|
||||
# you must disable the use of transactions as these methods can not run in an
|
||||
# existing transaction. When using "add_concurrent_index" make sure that this
|
||||
# method is the _only_ method called in the migration, any other changes
|
||||
# should go in a separate migration. This ensures that upon failure _only_ the
|
||||
# index creation fails and can be retried or reverted easily.
|
||||
#
|
||||
# To disable transactions uncomment the following line and remove these
|
||||
# comments:
|
||||
# disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all
|
||||
end
|
||||
end
|
32
db/schema.rb
32
db/schema.rb
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160705163108) do
|
||||
ActiveRecord::Schema.define(version: 20160712171823) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.string "recaptcha_site_key"
|
||||
t.string "recaptcha_private_key"
|
||||
t.integer "metrics_port", default: 8089
|
||||
t.boolean "akismet_enabled", default: false
|
||||
t.string "akismet_api_key"
|
||||
t.integer "metrics_sample_interval", default: 15
|
||||
t.boolean "sentry_enabled", default: false
|
||||
t.string "sentry_dsn"
|
||||
t.boolean "akismet_enabled", default: false
|
||||
t.string "akismet_api_key"
|
||||
t.boolean "email_author_in_body", default: false
|
||||
t.integer "default_group_visibility"
|
||||
t.boolean "repository_checks_enabled", default: false
|
||||
|
@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.string "health_check_access_token"
|
||||
t.boolean "send_user_confirmation_email", default: false
|
||||
t.integer "container_registry_token_expire_delay", default: 5
|
||||
t.boolean "user_default_external", default: false, null: false
|
||||
t.text "after_sign_up_text"
|
||||
t.string "repository_storage", default: "default"
|
||||
t.string "enabled_git_access_protocol"
|
||||
t.boolean "user_default_external", default: false, null: false
|
||||
end
|
||||
|
||||
create_table "audit_events", force: :cascade do |t|
|
||||
|
@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.text "artifacts_metadata"
|
||||
t.integer "erased_by_id"
|
||||
t.datetime "erased_at"
|
||||
t.string "environment"
|
||||
t.datetime "artifacts_expire_at"
|
||||
t.string "environment"
|
||||
t.integer "artifacts_size"
|
||||
end
|
||||
|
||||
|
@ -481,10 +481,11 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.string "state"
|
||||
t.integer "iid"
|
||||
t.integer "updated_by_id"
|
||||
t.integer "moved_to_id"
|
||||
t.boolean "confidential", default: false
|
||||
t.datetime "deleted_at"
|
||||
t.date "due_date"
|
||||
t.integer "moved_to_id"
|
||||
t.integer "lock_version", default: 0, null: false
|
||||
end
|
||||
|
||||
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
|
||||
|
@ -624,6 +625,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.integer "merge_user_id"
|
||||
t.string "merge_commit_sha"
|
||||
t.datetime "deleted_at"
|
||||
t.integer "lock_version", default: 0, null: false
|
||||
t.string "in_progress_merge_commit_sha"
|
||||
end
|
||||
|
||||
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
|
||||
|
@ -773,10 +776,10 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.integer "user_id", null: false
|
||||
t.string "token", null: false
|
||||
t.string "name", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "revoked", default: false
|
||||
t.datetime "expires_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
|
||||
|
@ -858,11 +861,12 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
|
||||
|
||||
create_table "protected_branches", force: :cascade do |t|
|
||||
t.integer "project_id", null: false
|
||||
t.string "name", null: false
|
||||
t.integer "project_id", null: false
|
||||
t.string "name", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.boolean "developers_can_push", default: false, null: false
|
||||
t.boolean "developers_can_push", default: false, null: false
|
||||
t.boolean "developers_can_merge", default: false, null: false
|
||||
end
|
||||
|
||||
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
|
||||
|
@ -896,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
|||
t.string "type"
|
||||
t.string "title"
|
||||
t.integer "project_id"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.boolean "active", default: false, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "active", null: false
|
||||
t.text "properties"
|
||||
t.boolean "template", default: false
|
||||
t.boolean "push_events", default: true
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
|
||||
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
|
||||
- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
|
||||
- [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
|
||||
- [Permissions](user/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
|
||||
- [Profile Settings](profile/README.md)
|
||||
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
|
||||
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
|
||||
|
|
|
@ -68,7 +68,9 @@ Parameters:
|
|||
"merge_when_build_succeeds": true,
|
||||
"merge_status": "can_be_merged",
|
||||
"subscribed" : false,
|
||||
"user_notes_count": 1
|
||||
"user_notes_count": 1,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
@ -132,7 +134,9 @@ Parameters:
|
|||
"merge_when_build_succeeds": true,
|
||||
"merge_status": "can_be_merged",
|
||||
"subscribed" : true,
|
||||
"user_notes_count": 1
|
||||
"user_notes_count": 1,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -233,6 +237,8 @@ Parameters:
|
|||
"merge_status": "can_be_merged",
|
||||
"subscribed" : true,
|
||||
"user_notes_count": 1,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false,
|
||||
"changes": [
|
||||
{
|
||||
"old_path": "VERSION",
|
||||
|
@ -312,7 +318,9 @@ Parameters:
|
|||
"merge_when_build_succeeds": true,
|
||||
"merge_status": "can_be_merged",
|
||||
"subscribed" : true,
|
||||
"user_notes_count": 0
|
||||
"user_notes_count": 0,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -383,7 +391,9 @@ Parameters:
|
|||
"merge_when_build_succeeds": true,
|
||||
"merge_status": "can_be_merged",
|
||||
"subscribed" : true,
|
||||
"user_notes_count": 1
|
||||
"user_notes_count": 1,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -481,7 +491,9 @@ Parameters:
|
|||
"merge_when_build_succeeds": true,
|
||||
"merge_status": "can_be_merged",
|
||||
"subscribed" : true,
|
||||
"user_notes_count": 1
|
||||
"user_notes_count": 1,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -547,7 +559,9 @@ Parameters:
|
|||
"merge_when_build_succeeds": true,
|
||||
"merge_status": "can_be_merged",
|
||||
"subscribed" : true,
|
||||
"user_notes_count": 1
|
||||
"user_notes_count": 1,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -866,7 +880,9 @@ Example response:
|
|||
"merge_when_build_succeeds": false,
|
||||
"merge_status": "unchecked",
|
||||
"subscribed": true,
|
||||
"user_notes_count": 7
|
||||
"user_notes_count": 7,
|
||||
"should_remove_source_branch": true,
|
||||
"force_remove_source_branch": false
|
||||
},
|
||||
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
|
||||
"body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
|
||||
|
|
|
@ -15,7 +15,7 @@ Parameters:
|
|||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, or `marked`. |
|
||||
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, or `approval_required`. |
|
||||
| `author_id` | integer | no | The ID of an author |
|
||||
| `project_id` | integer | no | The ID of a project |
|
||||
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
|
||||
|
|
|
@ -15,6 +15,6 @@
|
|||
- [Use SSH keys in your build environment](ssh_keys/README.md)
|
||||
- [Trigger builds through the API](triggers/README.md)
|
||||
- [Build artifacts](build_artifacts/README.md)
|
||||
- [User permissions](permissions/README.md)
|
||||
- [User permissions](../user/permissions.md#gitlab-ci)
|
||||
- [API](../api/ci/README.md)
|
||||
- [CI services (linked docker containers)](services/README.md)
|
||||
|
|
|
@ -1,24 +1,3 @@
|
|||
# Users Permissions
|
||||
|
||||
GitLab CI relies on user's role on the GitLab. There are three permissions levels on GitLab CI: admin, master, developer, other.
|
||||
|
||||
Admin user can perform any actions on GitLab CI in scope of instance and project. Also user with admin permission can use admin interface.
|
||||
|
||||
|
||||
|
||||
|
||||
| Action | Guest, Reporter | Developer | Master | Admin |
|
||||
|---------------------------------------|-----------------|-------------|----------|--------|
|
||||
| See commits and builds | ✓ | ✓ | ✓ | ✓ |
|
||||
| Retry or cancel build | | ✓ | ✓ | ✓ |
|
||||
| Remove project | | | ✓ | ✓ |
|
||||
| Create project | | | ✓ | ✓ |
|
||||
| Change project configuration | | | ✓ | ✓ |
|
||||
| Add specific runners | | | ✓ | ✓ |
|
||||
| Add shared runners | | | | ✓ |
|
||||
| See events in the system | | | | ✓ |
|
||||
| Admin interface | | | | ✓ |
|
||||
|
||||
|
||||
|
||||
|
||||
This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-ci).
|
||||
|
|
|
@ -27,6 +27,8 @@ We try to keep the amount of tabs in the header navigation between 5 and 10 so t
|
|||
tab should represent separate functionality. Everything related to the issue
|
||||
tracker should be under the 'Issues' tab while everything related to the wiki should
|
||||
be under 'Wiki' tab and so on and so forth.
|
||||
When adding a new tab to the header don't use more than 2 words for text in the link.
|
||||
We want to keep links short and easy to remember and fit all of them in the small screen.
|
||||
|
||||
## Mobile screen size
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ This setting is only available on GitLab 8.7 and above.
|
|||
|
||||
SAML login includes support for external groups. You can define in the SAML
|
||||
settings which groups, to which your users belong in your IdP, you wish to be
|
||||
marked as [external](../permissions/permissions.md).
|
||||
marked as [external](../user/permissions.md).
|
||||
|
||||
### Requirements
|
||||
|
||||
|
@ -306,4 +306,4 @@ For this you need take the following into account:
|
|||
validators are optional
|
||||
|
||||
Make sure that one of the above described scenarios is valid, or the requests will
|
||||
fail with one of the mentioned errors.
|
||||
fail with one of the mentioned errors.
|
||||
|
|
|
@ -1,104 +1,3 @@
|
|||
# Permissions
|
||||
|
||||
Users have different abilities depending on the access level they have in a particular group or project.
|
||||
|
||||
If a user is both in a project group and in the project itself, the highest permission level is used.
|
||||
|
||||
If a user is a GitLab administrator they receive all permissions.
|
||||
|
||||
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.
|
||||
|
||||
To add or import a user, you can follow the [project users and members
|
||||
documentation](../workflow/add-user/add-user.md).
|
||||
|
||||
## Project
|
||||
|
||||
| Action | Guest | Reporter | Developer | Master | Owner |
|
||||
|---------------------------------------|---------|------------|-------------|----------|--------|
|
||||
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Download project | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See environments | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage merge requests | | | ✓ | ✓ | ✓ |
|
||||
| Create new merge request | | | ✓ | ✓ | ✓ |
|
||||
| Create new branches | | | ✓ | ✓ | ✓ |
|
||||
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
|
||||
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
|
||||
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
|
||||
| Add tags | | | ✓ | ✓ | ✓ |
|
||||
| Write a wiki | | | ✓ | ✓ | ✓ |
|
||||
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
|
||||
| Create or update commit status | | | ✓ | ✓ | ✓ |
|
||||
| Update a container registry | | | ✓ | ✓ | ✓ |
|
||||
| Remove a container registry image | | | ✓ | ✓ | ✓ |
|
||||
| Create new environments | | | ✓ | ✓ | ✓ |
|
||||
| Create new milestones | | | | ✓ | ✓ |
|
||||
| Add new team members | | | | ✓ | ✓ |
|
||||
| Push to protected branches | | | | ✓ | ✓ |
|
||||
| Enable/disable branch protection | | | | ✓ | ✓ |
|
||||
| Turn on/off prot. branch push for devs| | | | ✓ | ✓ |
|
||||
| Rewrite/remove git tags | | | | ✓ | ✓ |
|
||||
| Edit project | | | | ✓ | ✓ |
|
||||
| Add deploy keys to project | | | | ✓ | ✓ |
|
||||
| Configure project hooks | | | | ✓ | ✓ |
|
||||
| Manage runners | | | | ✓ | ✓ |
|
||||
| Manage build triggers | | | | ✓ | ✓ |
|
||||
| Manage variables | | | | ✓ | ✓ |
|
||||
| Delete environments | | | | ✓ | ✓ |
|
||||
| Switch visibility level | | | | | ✓ |
|
||||
| Transfer project to another namespace | | | | | ✓ |
|
||||
| Remove project | | | | | ✓ |
|
||||
| Force push to protected branches [^2] | | | | | |
|
||||
| Remove protected branches [^2] | | | | | |
|
||||
|
||||
[^1]: If **Allow guest to access builds** is enabled in CI settings
|
||||
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
|
||||
|
||||
## Group
|
||||
|
||||
In order for a group to appear as public and be browsable, it must contain at
|
||||
least one public project.
|
||||
|
||||
Any user can remove themselves from a group, unless they are the last Owner of the group.
|
||||
|
||||
| Action | Guest | Reporter | Developer | Master | Owner |
|
||||
|-------------------------|-------|----------|-----------|--------|-------|
|
||||
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Edit group | | | | | ✓ |
|
||||
| Create project in group | | | | ✓ | ✓ |
|
||||
| Manage group members | | | | | ✓ |
|
||||
| Remove group | | | | | ✓ |
|
||||
|
||||
## External Users
|
||||
|
||||
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
|
||||
feature may be useful when for example a contractor is working on a given
|
||||
project and should only have access to that project.
|
||||
|
||||
External users can only access projects to which they are explicitly granted
|
||||
access, thus hiding all other internal or private ones from them. Access can be
|
||||
granted by adding the user as member to the project or group.
|
||||
|
||||
They will, like usual users, receive a role in the project or group with all
|
||||
the abilities that are mentioned in the table above. They cannot however create
|
||||
groups or projects, and they have the same access as logged out users in all
|
||||
other cases.
|
||||
|
||||
An administrator can flag a user as external [through the API](../api/users.md)
|
||||
or by checking the checkbox on the admin panel. As an administrator, navigate
|
||||
to **Admin > Users** to create a new user or edit an existing one. There, you
|
||||
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 an administrator under **Admin > Application Settings**.
|
||||
This document was moved to [user/permissions.md](../user/permissions.md).
|
||||
|
|
|
@ -17,7 +17,7 @@ Public projects can be cloned **without any** authentication.
|
|||
|
||||
They will also be listed on the public access directory (`/public`).
|
||||
|
||||
**Any logged in user** will have [Guest](../permissions/permissions.md)
|
||||
**Any logged in user** will have [Guest](../user/permissions.md)
|
||||
permissions on the repository.
|
||||
|
||||
### Internal projects
|
||||
|
@ -27,7 +27,7 @@ Internal projects can be cloned by any logged in user.
|
|||
They will also be listed on the public access directory (`/public`) for logged
|
||||
in users.
|
||||
|
||||
Any logged in user will have [Guest](../permissions/permissions.md) permissions
|
||||
Any logged in user will have [Guest](../user/permissions.md) permissions
|
||||
on the repository.
|
||||
|
||||
### How to change project visibility
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
# Permissions
|
||||
|
||||
Users have different abilities depending on the access level they have in a
|
||||
particular group or project. If a user is both in a group's project and the
|
||||
project itself, the highest permission level is used.
|
||||
|
||||
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.
|
||||
|
||||
GitLab administrators receive all permissions.
|
||||
|
||||
To add or import a user, you can follow the [project users and members
|
||||
documentation](../workflow/add-user/add-user.md).
|
||||
|
||||
## Project
|
||||
|
||||
The following table depicts the various user permission levels in a project.
|
||||
|
||||
| Action | Guest | Reporter | Developer | Master | Owner |
|
||||
|---------------------------------------|---------|------------|-------------|----------|--------|
|
||||
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Download project | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See environments | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage/Accept merge requests | | | ✓ | ✓ | ✓ |
|
||||
| Create new merge request | | | ✓ | ✓ | ✓ |
|
||||
| Create new branches | | | ✓ | ✓ | ✓ |
|
||||
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
|
||||
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
|
||||
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
|
||||
| Add tags | | | ✓ | ✓ | ✓ |
|
||||
| Write a wiki | | | ✓ | ✓ | ✓ |
|
||||
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
|
||||
| Create or update commit status | | | ✓ | ✓ | ✓ |
|
||||
| Update a container registry | | | ✓ | ✓ | ✓ |
|
||||
| Remove a container registry image | | | ✓ | ✓ | ✓ |
|
||||
| Create new environments | | | ✓ | ✓ | ✓ |
|
||||
| Create new milestones | | | | ✓ | ✓ |
|
||||
| Add new team members | | | | ✓ | ✓ |
|
||||
| Push to protected branches | | | | ✓ | ✓ |
|
||||
| Enable/disable branch protection | | | | ✓ | ✓ |
|
||||
| Turn on/off protected branch push for devs| | | | ✓ | ✓ |
|
||||
| Rewrite/remove Git tags | | | | ✓ | ✓ |
|
||||
| Edit project | | | | ✓ | ✓ |
|
||||
| Add deploy keys to project | | | | ✓ | ✓ |
|
||||
| Configure project hooks | | | | ✓ | ✓ |
|
||||
| Manage runners | | | | ✓ | ✓ |
|
||||
| Manage build triggers | | | | ✓ | ✓ |
|
||||
| Manage variables | | | | ✓ | ✓ |
|
||||
| Delete environments | | | | ✓ | ✓ |
|
||||
| Switch visibility level | | | | | ✓ |
|
||||
| Transfer project to another namespace | | | | | ✓ |
|
||||
| Remove project | | | | | ✓ |
|
||||
| Force push to protected branches [^2] | | | | | |
|
||||
| Remove protected branches [^2] | | | | | |
|
||||
|
||||
[^1]: If **Allow guest to access builds** is enabled in CI settings
|
||||
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
|
||||
|
||||
## Group
|
||||
|
||||
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
|
||||
group.
|
||||
|
||||
| Action | Guest | Reporter | Developer | Master | Owner |
|
||||
|-------------------------|-------|----------|-----------|--------|-------|
|
||||
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Edit group | | | | | ✓ |
|
||||
| Create project in group | | | | ✓ | ✓ |
|
||||
| Manage group members | | | | | ✓ |
|
||||
| Remove group | | | | | ✓ |
|
||||
|
||||
## External Users
|
||||
|
||||
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
|
||||
feature may be useful when for example a contractor is working on a given
|
||||
project and should only have access to that project.
|
||||
|
||||
External users can only access projects to which they are explicitly granted
|
||||
access, thus hiding all other internal or private ones from them. Access can be
|
||||
granted by adding the user as member to the project or group.
|
||||
|
||||
They will, like usual users, receive a role in the project or group with all
|
||||
the abilities that are mentioned in the table above. They cannot however create
|
||||
groups or projects, and they have the same access as logged out users in all
|
||||
other cases.
|
||||
|
||||
An administrator can flag a user as external [through the API](../api/users.md)
|
||||
or by checking the checkbox on the admin panel. As an administrator, navigate
|
||||
to **Admin > Users** to create a new user or edit an existing one. There, you
|
||||
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 an administrator under **Admin > Application Settings**.
|
||||
|
||||
## GitLab CI
|
||||
|
||||
GitLab CI permissions rely on the role the user has in GitLab. There are four
|
||||
permission levels it total:
|
||||
|
||||
- admin
|
||||
- master
|
||||
- developer
|
||||
- guest/reporter
|
||||
|
||||
The admin user can perform any action on GitLab CI in scope of the GitLab
|
||||
instance and project. In addition, all admins can use the admin interface under
|
||||
`/admin/runners`.
|
||||
|
||||
| Action | Guest, Reporter | Developer | Master | Admin |
|
||||
|---------------------------------------|-----------------|-------------|----------|--------|
|
||||
| See commits and builds | ✓ | ✓ | ✓ | ✓ |
|
||||
| Retry or cancel build | | ✓ | ✓ | ✓ |
|
||||
| Remove project | | | ✓ | ✓ |
|
||||
| Create project | | | ✓ | ✓ |
|
||||
| Change project configuration | | | ✓ | ✓ |
|
||||
| Add specific runners | | | ✓ | ✓ |
|
||||
| Add shared runners | | | | ✓ |
|
||||
| See events in the system | | | | ✓ |
|
||||
| Admin interface | | | | ✓ |
|
|
@ -23,7 +23,7 @@ want to add.
|
|||
|
||||
---
|
||||
|
||||
Select the user and the [permission level](../../permissions/permissions.md)
|
||||
Select the user and the [permission level](../../user/permissions.md)
|
||||
that you'd like to give the user. Note that you can select more than one user.
|
||||
|
||||
![Give user permissions](img/add_user_give_permissions.png)
|
||||
|
|
|
@ -38,7 +38,7 @@ Forking a project is in most cases a two-step process.
|
|||
---
|
||||
|
||||
After the forking is done, you can start working on the newly created
|
||||
repository. There, you will have full [Owner](../permissions/permissions.md)
|
||||
repository. There, you will have full [Owner](../user/permissions.md)
|
||||
access, so you can set it up as you please.
|
||||
|
||||
## Merging upstream
|
||||
|
|
|
@ -12,7 +12,7 @@ A protected branch does three simple things:
|
|||
|
||||
You can make any branch a protected branch. GitLab makes the master branch a protected branch by default.
|
||||
|
||||
To protect a branch, user needs to have at least a Master permission level, see [permissions document](../permissions/permissions.md).
|
||||
To protect a branch, user needs to have at least a Master permission level, see [permissions document](../user/permissions.md).
|
||||
|
||||
![protected branches page](protected_branches/protected_branches1.png)
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ Feature: Project Merge Requests
|
|||
Then The list should be sorted by "Oldest updated"
|
||||
|
||||
@javascript
|
||||
Scenario: Visiting Merge Requests from a differente Project after sorting
|
||||
Scenario: Visiting Merge Requests from a different Project after sorting
|
||||
Given I visit project "Shop" merge requests page
|
||||
And I sort the list by "Oldest updated"
|
||||
And I visit dashboard merge requests page
|
||||
|
|
|
@ -207,6 +207,8 @@ module API
|
|||
merge_request.subscribed?(options[:current_user])
|
||||
end
|
||||
expose :user_notes_count
|
||||
expose :should_remove_source_branch?, as: :should_remove_source_branch
|
||||
expose :force_remove_source_branch?, as: :force_remove_source_branch
|
||||
end
|
||||
|
||||
class MergeRequestChanges < MergeRequest
|
||||
|
|
|
@ -17,7 +17,7 @@ module API
|
|||
def current_user
|
||||
@current_user ||= (find_user_by_private_token || doorkeeper_guard)
|
||||
|
||||
unless @current_user && Gitlab::UserAccess.allowed?(@current_user)
|
||||
unless @current_user && Gitlab::UserAccess.new(@current_user).allowed?
|
||||
return nil
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@ module Banzai
|
|||
Renderer.render(text, context)
|
||||
end
|
||||
|
||||
def self.cache_collection_render(texts_and_contexts)
|
||||
Renderer.cache_collection_render(texts_and_contexts)
|
||||
end
|
||||
|
||||
def self.render_result(text, context = {})
|
||||
Renderer.render_result(text, context)
|
||||
end
|
||||
|
|
|
@ -39,9 +39,7 @@ module Banzai
|
|||
|
||||
# Renders the attribute of every given object.
|
||||
def render_objects(objects, attribute)
|
||||
objects.map do |object|
|
||||
render_attribute(object, attribute)
|
||||
end
|
||||
render_attributes(objects, attribute)
|
||||
end
|
||||
|
||||
# Redacts the list of documents.
|
||||
|
@ -64,16 +62,21 @@ module Banzai
|
|||
context
|
||||
end
|
||||
|
||||
# Renders the attribute of an object.
|
||||
# Renders the attributes of a set of objects.
|
||||
#
|
||||
# Returns a `Nokogiri::HTML::Document`.
|
||||
def render_attribute(object, attribute)
|
||||
context = context_for(object, attribute)
|
||||
# Returns an Array of `Nokogiri::HTML::Document`.
|
||||
def render_attributes(objects, attribute)
|
||||
strings_and_contexts = objects.map do |object|
|
||||
context = context_for(object, attribute)
|
||||
|
||||
string = object.__send__(attribute)
|
||||
html = Banzai.render(string, context)
|
||||
string = object.__send__(attribute)
|
||||
|
||||
Banzai::Pipeline[:relative_link].to_document(html, context)
|
||||
{ text: string, context: context }
|
||||
end
|
||||
|
||||
Banzai.cache_collection_render(strings_and_contexts).each_with_index.map do |html, index|
|
||||
Banzai::Pipeline[:relative_link].to_document(html, strings_and_contexts[index][:context])
|
||||
end
|
||||
end
|
||||
|
||||
def base_context
|
||||
|
|
|
@ -10,7 +10,7 @@ module Banzai
|
|||
# requiring XHTML, such as Atom feeds, need to call `post_process` on the
|
||||
# result, providing the appropriate `pipeline` option.
|
||||
#
|
||||
# markdown - Markdown String
|
||||
# text - Markdown String
|
||||
# context - Hash of context options passed to our HTML Pipeline
|
||||
#
|
||||
# Returns an HTML-safe String
|
||||
|
@ -29,6 +29,58 @@ module Banzai
|
|||
end
|
||||
end
|
||||
|
||||
# Perform multiple render from an Array of Markdown String into an
|
||||
# Array of HTML-safe String of HTML.
|
||||
#
|
||||
# As the rendered Markdown String can be already cached read all the data
|
||||
# from the cache using Rails.cache.read_multi operation. If the Markdown String
|
||||
# is not in the cache or it's not cacheable (no cache_key entry is provided in
|
||||
# the context) the Markdown String is rendered and stored in the cache so the
|
||||
# next render call gets the rendered HTML-safe String from the cache.
|
||||
#
|
||||
# For further explanation see #render method comments.
|
||||
#
|
||||
# texts_and_contexts - An Array of Hashes that contains the Markdown String (:text)
|
||||
# an options passed to our HTML Pipeline (:context)
|
||||
#
|
||||
# If on the :context you specify a :cache_key entry will be used to retrieve it
|
||||
# and cache the result of rendering the Markdown String.
|
||||
#
|
||||
# Returns an Array containing HTML-safe String instances.
|
||||
#
|
||||
# Example:
|
||||
# texts_and_contexts
|
||||
# => [{ text: '### Hello',
|
||||
# context: { cache_key: [note, :note] } }]
|
||||
def self.cache_collection_render(texts_and_contexts)
|
||||
items_collection = texts_and_contexts.each_with_index do |item, index|
|
||||
context = item[:context]
|
||||
cache_key = full_cache_multi_key(context.delete(:cache_key), context[:pipeline])
|
||||
|
||||
item[:cache_key] = cache_key if cache_key
|
||||
end
|
||||
|
||||
cacheable_items, non_cacheable_items = items_collection.partition { |item| item.key?(:cache_key) }
|
||||
|
||||
items_in_cache = []
|
||||
items_not_in_cache = []
|
||||
|
||||
unless cacheable_items.empty?
|
||||
items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] })
|
||||
items_not_in_cache = cacheable_items.reject do |item|
|
||||
item[:rendered] = items_in_cache[item[:cache_key]]
|
||||
items_in_cache.key?(item[:cache_key])
|
||||
end
|
||||
end
|
||||
|
||||
(items_not_in_cache + non_cacheable_items).each do |item|
|
||||
item[:rendered] = render(item[:text], item[:context])
|
||||
Rails.cache.write(item[:cache_key], item[:rendered]) if item[:cache_key]
|
||||
end
|
||||
|
||||
items_collection.map { |item| item[:rendered] }
|
||||
end
|
||||
|
||||
def self.render_result(text, context = {})
|
||||
text = Pipeline[:pre_process].to_html(text, context) if text
|
||||
|
||||
|
@ -78,5 +130,13 @@ module Banzai
|
|||
return unless cache_key
|
||||
["banzai", *cache_key, pipeline_name || :full]
|
||||
end
|
||||
|
||||
# To map Rails.cache.read_multi results we need to know the Rails.cache.expanded_key.
|
||||
# Other option will be to generate stringified keys on our side and don't delegate to Rails.cache.expanded_key
|
||||
# method.
|
||||
def self.full_cache_multi_key(cache_key, pipeline_name)
|
||||
return unless cache_key
|
||||
Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,9 +14,10 @@ module Gitlab
|
|||
OWNER = 50
|
||||
|
||||
# Branch protection settings
|
||||
PROTECTION_NONE = 0
|
||||
PROTECTION_DEV_CAN_PUSH = 1
|
||||
PROTECTION_FULL = 2
|
||||
PROTECTION_NONE = 0
|
||||
PROTECTION_DEV_CAN_PUSH = 1
|
||||
PROTECTION_FULL = 2
|
||||
PROTECTION_DEV_CAN_MERGE = 3
|
||||
|
||||
class << self
|
||||
def values
|
||||
|
@ -54,6 +55,7 @@ module Gitlab
|
|||
def protection_options
|
||||
{
|
||||
"Not protected: Both developers and masters can push new commits, force push, or delete the branch." => PROTECTION_NONE,
|
||||
"Protected against pushes: Developers cannot push new commits, but are allowed to accept merge requests to the branch." => PROTECTION_DEV_CAN_MERGE,
|
||||
"Partially protected: Developers can push new commits, but cannot force push or delete the branch. Masters can do all of those." => PROTECTION_DEV_CAN_PUSH,
|
||||
"Fully protected: Developers cannot push new commits, force push, or delete the branch. Only masters can do any of those." => PROTECTION_FULL,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
module Gitlab
|
||||
module Checks
|
||||
class ChangeAccess
|
||||
attr_reader :user_access, :project
|
||||
|
||||
def initialize(change, user_access:, project:)
|
||||
@oldrev, @newrev, @ref = change.split(' ')
|
||||
@branch_name = branch_name(@ref)
|
||||
@user_access = user_access
|
||||
@project = project
|
||||
end
|
||||
|
||||
def exec
|
||||
error = protected_branch_checks || tag_checks || push_checks
|
||||
|
||||
if error
|
||||
GitAccessStatus.new(false, error)
|
||||
else
|
||||
GitAccessStatus.new(true)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def protected_branch_checks
|
||||
return unless project.protected_branch?(@branch_name)
|
||||
|
||||
if forced_push? && user_access.cannot_do_action?(:force_push_code_to_protected_branches)
|
||||
return "You are not allowed to force push code to a protected branch on this project."
|
||||
elsif Gitlab::Git.blank_ref?(@newrev) && user_access.cannot_do_action?(:remove_protected_branches)
|
||||
return "You are not allowed to delete protected branches from this project."
|
||||
end
|
||||
|
||||
if matching_merge_request?
|
||||
if user_access.can_merge_to_branch?(@branch_name) || user_access.can_push_to_branch?(@branch_name)
|
||||
return
|
||||
else
|
||||
"You are not allowed to merge code into protected branches on this project."
|
||||
end
|
||||
else
|
||||
if user_access.can_push_to_branch?(@branch_name)
|
||||
return
|
||||
else
|
||||
"You are not allowed to push code to protected branches on this project."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def tag_checks
|
||||
tag_ref = tag_name(@ref)
|
||||
|
||||
if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project)
|
||||
"You are not allowed to change existing tags on this project."
|
||||
end
|
||||
end
|
||||
|
||||
def push_checks
|
||||
if user_access.cannot_do_action?(:push_code)
|
||||
"You are not allowed to push code to this project."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def protected_tag?(tag_name)
|
||||
project.repository.tag_exists?(tag_name)
|
||||
end
|
||||
|
||||
def forced_push?
|
||||
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
|
||||
end
|
||||
|
||||
def matching_merge_request?
|
||||
Checks::MatchingMergeRequest.new(@newrev, @branch_name, @project).match?
|
||||
end
|
||||
|
||||
def branch_name(ref)
|
||||
ref = @ref.to_s
|
||||
if Gitlab::Git.branch_ref?(ref)
|
||||
Gitlab::Git.ref_name(ref)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def tag_name(ref)
|
||||
ref = @ref.to_s
|
||||
if Gitlab::Git.tag_ref?(ref)
|
||||
Gitlab::Git.ref_name(ref)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module Gitlab
|
||||
module Checks
|
||||
class ForcePush
|
||||
def self.force_push?(project, oldrev, newrev)
|
||||
return false if project.empty_repo?
|
||||
|
||||
# Created or deleted branch
|
||||
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
|
||||
false
|
||||
else
|
||||
missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
|
||||
missed_refs.split("\n").size > 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
module Gitlab
|
||||
module Checks
|
||||
class MatchingMergeRequest
|
||||
def initialize(newrev, branch_name, project)
|
||||
@newrev = newrev
|
||||
@branch_name = branch_name
|
||||
@project = project
|
||||
end
|
||||
|
||||
def match?
|
||||
@project.merge_requests
|
||||
.with_state(:locked)
|
||||
.where(in_progress_merge_commit_sha: @newrev, target_branch: @branch_name)
|
||||
.exists?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,16 +1,30 @@
|
|||
module Gitlab
|
||||
module Diff
|
||||
class InlineDiff
|
||||
# Regex to find a run of deleted lines followed by the same number of added lines
|
||||
LINE_PAIRS_PATTERN = %r{
|
||||
# Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
|
||||
(?:\A|\s)
|
||||
|
||||
# This matches a number of `-`s followed by the same number of `+`s through recursion
|
||||
(?<del_ins>
|
||||
-
|
||||
\g<del_ins>?
|
||||
\+
|
||||
)
|
||||
|
||||
# Runs end at the end of the string (the last line) or before a space (for an unchanged line)
|
||||
(?=\s|\z)
|
||||
}x.freeze
|
||||
|
||||
attr_accessor :old_line, :new_line, :offset
|
||||
|
||||
def self.for_lines(lines)
|
||||
local_edit_indexes = self.find_local_edits(lines)
|
||||
changed_line_pairs = self.find_changed_line_pairs(lines)
|
||||
|
||||
inline_diffs = []
|
||||
|
||||
local_edit_indexes.each do |index|
|
||||
old_index = index
|
||||
new_index = index + 1
|
||||
changed_line_pairs.each do |old_index, new_index|
|
||||
old_line = lines[old_index]
|
||||
new_line = lines[new_index]
|
||||
|
||||
|
@ -51,18 +65,28 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def self.find_local_edits(lines)
|
||||
line_prefixes = lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' }
|
||||
joined_line_prefixes = " #{line_prefixes.join} "
|
||||
# Finds pairs of old/new line pairs that represent the same line that changed
|
||||
def self.find_changed_line_pairs(lines)
|
||||
# Prefixes of all diff lines, indicating their types
|
||||
# For example: `" - + -+ ---+++ --+ -++"`
|
||||
line_prefixes = lines.each_with_object("") { |line, s| s << line[0] }.gsub(/[^ +-]/, ' ')
|
||||
|
||||
offset = 0
|
||||
local_edit_indexes = []
|
||||
while index = joined_line_prefixes.index(" -+ ", offset)
|
||||
local_edit_indexes << index
|
||||
offset = index + 1
|
||||
changed_line_pairs = []
|
||||
line_prefixes.scan(LINE_PAIRS_PATTERN) do
|
||||
# For `"---+++"`, `begin_index == 0`, `end_index == 6`
|
||||
begin_index, end_index = Regexp.last_match.offset(:del_ins)
|
||||
|
||||
# For `"---+++"`, `changed_line_count == 3`
|
||||
changed_line_count = (end_index - begin_index) / 2
|
||||
|
||||
halfway_index = begin_index + changed_line_count
|
||||
(begin_index...halfway_index).each do |i|
|
||||
# For `"---+++"`, index 1 maps to 1 + 3 = 4
|
||||
changed_line_pairs << [i, i + changed_line_count]
|
||||
end
|
||||
end
|
||||
|
||||
local_edit_indexes
|
||||
changed_line_pairs
|
||||
end
|
||||
|
||||
def longest_common_prefix(a, b)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue