Commit c3b81e5f authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of github.com:gitlabhq/gitlabhq

parents 3f4cfd36 8ad9a75f
......@@ -101,7 +101,11 @@ For examples of feedback on merge requests please look at already [closed merge
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging.
1. It conforms to the following style guides
1. It conforms to the following style guides.
If your change touches a line that does not follow the style,
modify the entire line to follow it. This prevents linting tools from generating warnings.
Don't touch neighbouring lines. As an exception, automatic mass refactoring modifications
may leave style non-compliant.
## Style guides
......
class Activities
class @Activities
constructor: ->
Pager.init 20, true
$(".event_filter_link").bind "click", (event) =>
......@@ -27,5 +27,3 @@ class Activities
event_filters.splice index, 1
$.cookie "event_filter", event_filters.join(","), { path: '/' }
@Activities = Activities
class Admin
class @Admin
constructor: ->
$('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation')
......@@ -51,5 +51,3 @@ class Admin
$('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href)
@Admin = Admin
class BlobView
class @BlobView
constructor: ->
# handle multi-line select
handleMultiSelect = (e) ->
......@@ -71,6 +71,3 @@ class BlobView
# Highlight the correct lines when the hash part of the URL changes
$(window).on("hashchange", highlightBlobLines)
@BlobView = BlobView
class Commit
class @Commit
constructor: ->
$('.files .diff-file').each ->
new CommitFile(this)
@Commit = Commit
class CommitFile
class @CommitFile
constructor: (file) ->
if $('.image', file).length
new ImageFile(file)
@CommitFile = CommitFile
class ImageFile
class @ImageFile
# Width where images must fits in, for 2-up this gets divided by 2
@availWidth = 900
......@@ -124,5 +124,3 @@ class ImageFile
else
img.on 'load', =>
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
@ImageFile = ImageFile
class CommitsList
class @CommitsList
@data =
ref: null
limit: 0
......@@ -53,5 +53,3 @@ class CommitsList
@disable
callback: =>
this.getOld()
this.CommitsList = CommitsList
class ConfirmDangerModal
class @ConfirmDangerModal
constructor: (form, text) ->
@form = form
$('.js-confirm-text').text(text || '')
......@@ -16,5 +16,3 @@ class ConfirmDangerModal
$('.js-confirm-danger-submit').on 'click', =>
@form.submit()
@ConfirmDangerModal = ConfirmDangerModal
class Dashboard
class @Dashboard
constructor: ->
@initSidebarTab()
......@@ -28,6 +28,3 @@ class Dashboard
# show tab from cookie
sidebar_filter = $.cookie(key)
$("#" + sidebar_filter).tab('show') if sidebar_filter
@Dashboard = Dashboard
class Diff
class @Diff
UNFOLD_COUNT = 20
constructor: ->
$(document).on('click', '.js-unfold', (event) =>
......@@ -41,6 +41,3 @@ class Diff
lines = line.children().slice(0, 2)
line_numbers = ($(l).attr('data-linenumber') for l in lines)
(parseInt(line_number) for line_number in line_numbers)
@Diff = Diff
class Flash
class @Flash
constructor: (message, type)->
flash = $(".flash-container")
flash.html("")
......@@ -10,5 +10,3 @@ class Flash
flash.click -> $(@).fadeOut()
flash.show()
@Flash = Flash
class GroupMembers
class @GroupMembers
constructor: ->
$('li.group_member').bind 'ajax:success', ->
$(this).fadeOut()
@GroupMembers = GroupMembers
$ ->
# avatar
$('.js-choose-group-avatar-button').bind "click", ->
......
class Issue
class @Issue
constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
......@@ -15,5 +15,3 @@ class Issue
"issue"
updateTaskState
)
@Issue = Issue
class Labels
class @Labels
constructor: ->
form = $('.label-form')
@setupLabelForm(form)
......@@ -31,5 +31,3 @@ class Labels
# Notify the form, that color has changed
$('.label-form').trigger('keyup')
e.preventDefault()
@Labels = Labels
class MergeRequest
class @MergeRequest
constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request')
......@@ -132,5 +132,3 @@ class MergeRequest
this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
this.MergeRequest = MergeRequest
class Milestone
class @Milestone
@updateIssue: (li, issue_url, data) ->
$.ajax
type: "PUT"
......@@ -115,5 +115,3 @@ class Milestone
Milestone.updateMergeRequest(ui.item, merge_request_url, data)
).disableSelection()
@Milestone = Milestone
class Notes
class @Notes
@interval: null
constructor: (notes_url, note_ids, last_fetched_at) ->
......@@ -514,7 +514,3 @@ class Notes
else
form.find('.js-note-target-reopen').text('Reopen')
form.find('.js-note-target-close').text('Close')
@Notes = Notes
class NotesVotes
class @NotesVotes
updateVotes: ->
votes = $("#votes .votes")
notes = $("#notes-list .note .vote")
......@@ -18,5 +18,3 @@ class NotesVotes
# replace vote numbers
votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes)
votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes)
@NotesVotes = NotesVotes
class Project
class @Project
constructor: ->
$('.project-edit-container').on 'ajax:before', =>
$('.project-edit-container').hide()
......@@ -24,9 +24,6 @@ class Project
else
$('#project_issues_tracker_id').removeAttr('disabled')
@Project = Project
$ ->
# Git clone panel switcher
scope = $ '.git-clone-holder'
......
class ProjectImport
class @ProjectImport
constructor: ->
setTimeout ->
Turbolinks.visit(location.href)
, 5000
@ProjectImport = ProjectImport
class SearchAutocomplete
class @SearchAutocomplete
constructor: (search_autocomplete_path, project_id, project_ref) ->
project_id = '' unless project_id
project_ref = '' unless project_ref
......@@ -9,5 +9,3 @@ class SearchAutocomplete
minLength: 1
select: (event, ui) ->
location.href = ui.item.url
@SearchAutocomplete = SearchAutocomplete
class window.StatGraph
class @StatGraph
@log: {}
@get_log: ->
@log
......
class window.ContributorsStatGraph
class @ContributorsStatGraph
init: (log) ->
@parsed_log = ContributorsStatGraphUtil.parse_log(log)
@set_current_field("commits")
......
class window.ContributorsGraph
class @ContributorsGraph
MARGIN:
top: 20
right: 20
......@@ -44,7 +44,7 @@ class window.ContributorsGraph
set_data: (data) ->
@data = data
class window.ContributorsMasterGraph extends ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width() - 70
@height = 200
......@@ -117,7 +117,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph
@svg.select("path").attr("d", @area)
@svg.select(".y.axis").call(@y_axis)
class window.ContributorsAuthorGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width()/2 - 100
@height = 200
......
class TeamMembers
class @TeamMembers
constructor: ->
$('.team-members .project-access-select').on "change", ->
$(this.form).submit()
@TeamMembers = TeamMembers
class TreeView
class @TreeView
constructor: ->
@initKeyNav()
......@@ -39,5 +39,3 @@ class TreeView
else if e.which is 13
path = $('.tree-item.selected .tree-item-file-name a').attr('href')
Turbolinks.visit(path)
@TreeView = TreeView
class Wikis
class @Wikis
constructor: ->
$('.build-new-wiki').bind "click", ->
field = $('#new_wiki_path')
......@@ -7,6 +7,3 @@ class Wikis
if(slug.length > 0)
location.href = path + "/" + slug
@Wikis = Wikis
......@@ -31,17 +31,15 @@ class Admin::ProjectsController < Admin::ApplicationController
protected
def project
id = params[:project_id] || params[:id]
@project = Project.find_with_namespace(id)
@project = Project.find_with_namespace(params[:id])
@project || render_404
end
def group
@group ||= project.group
@group ||= @project.group
end
def repository
@repository ||= project.repository
@repository ||= @project.repository
end
end
......@@ -81,28 +81,31 @@ class ApplicationController < ActionController::Base
end
def project
id = params[:project_id] || params[:id]
# Redirect from
# localhost/group/project.git
# to
# localhost/group/project
#
if id =~ /\.git\Z/
redirect_to request.original_url.gsub(/\.git\Z/, '') and return
end
unless @project
id = params[:project_id] || params[:id]
# Redirect from
# localhost/group/project.git
# to
# localhost/group/project
#
if id =~ /\.git\Z/
redirect_to request.original_url.gsub(/\.git\Z/, '') and return
end
@project = Project.find_with_namespace(id)
@project = Project.find_with_namespace(id)
if @project and can?(current_user, :read_project, @project)
@project
elsif current_user.nil?
@project = nil
authenticate_user!
else
@project = nil
render_404 and return
if @project and can?(current_user, :read_project, @project)
@project
elsif current_user.nil?
@project = nil
authenticate_user!
else
@project = nil
render_404 and return
end
end
@project
end
def repository
......@@ -119,14 +122,6 @@ class ApplicationController < ActionController::Base
return access_denied! unless can?(current_user, action, project)
end
def authorize_code_access!
return access_denied! unless can?(current_user, :download_code, project)
end
def authorize_push!
return access_denied! unless can?(current_user, :push_code, project)
end
def authorize_labels!
# Labels should be accessible for issues and/or merge requests
authorize_read_issue! || authorize_read_merge_request!
......
......@@ -2,7 +2,7 @@ class Projects::BaseTreeController < Projects::ApplicationController
include ExtractsPath
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
end
......@@ -4,7 +4,7 @@ class Projects::BlameController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
......
......@@ -4,9 +4,9 @@ class Projects::BlobController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :authorize_push!, only: [:destroy]
before_filter :authorize_push_code!, only: [:destroy]
before_filter :blob
......
......@@ -3,8 +3,8 @@ class Projects::BranchesController < Projects::ApplicationController
before_filter :authorize_read_project!
before_filter :require_non_empty_project
before_filter :authorize_code_access!
before_filter :authorize_push!, only: [:create, :destroy]
before_filter :authorize_download_code!
before_filter :authorize_push_code!, only: [:create, :destroy]
def index
@sort = params[:sort] || 'name'
......
......@@ -4,19 +4,19 @@
class Projects::CommitController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :commit
def show
return git_not_found! unless @commit
@line_notes = project.notes.for_commit_id(commit.id).inline
@branches = project.repository.branch_names_contains(commit.id)
@line_notes = @project.notes.for_commit_id(commit.id).inline
@branches = @project.repository.branch_names_contains(commit.id)
@diffs = @commit.diffs
@note = project.build_commit_note(commit)
@notes_count = project.notes.for_commit_id(commit.id).count
@notes = project.notes.for_commit_id(@commit.id).not_inline.fresh
@note = @project.build_commit_note(commit)
@notes_count = @project.notes.for_commit_id(commit.id).count
@notes = @project.notes.for_commit_id(@commit.id).not_inline.fresh
@noteable = @commit
@comments_allowed = @reply_allowed = true
@comments_target = {
......@@ -32,6 +32,6 @@ class Projects::CommitController < Projects::ApplicationController
end
def commit
@commit ||= project.repository.commit(params[:id])
@commit ||= @project.repository.commit(params[:id])
end
end
......@@ -5,7 +5,7 @@ class Projects::CommitsController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
......
class Projects::CompareController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def index
......
......@@ -42,7 +42,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def enable
project.deploy_keys << available_keys.find(params[:id])
@project.deploy_keys << available_keys.find(params[:id])
redirect_to project_deploy_keys_path(@project)
end
......
class Projects::EditTreeController < Projects::BaseTreeController
before_filter :require_branch_head
before_filter :blob
before_filter :authorize_push!
before_filter :authorize_push_code!
before_filter :from_merge_request
before_filter :after_edit_path
......
class Projects::GraphsController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
......
......@@ -4,7 +4,7 @@ class Projects::NetworkController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
......
class Projects::NewTreeController < Projects::BaseTreeController
before_filter :require_branch_head
before_filter :authorize_push!
before_filter :authorize_push_code!
def show
end
......
......@@ -4,7 +4,7 @@ class Projects::RawController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
......
......@@ -3,7 +3,7 @@ class Projects::RefsController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def switch
......
class Projects::RepositoriesController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def archive
......
......@@ -3,8 +3,8 @@ class Projects::TagsController < Projects::ApplicationController
before_filter :authorize_read_project!
before_filter :require_non_empty_project
before_filter :authorize_code_access!
before_filter :authorize_push!, only: [:create]
before_filter :authorize_download_code!
before_filter :authorize_push_code!, only: [:create]
before_filter :authorize_admin_project!, only: [:destroy]
def index
......
......@@ -10,7 +10,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end
def new
@user_project_relation = project.project_members.new
@user_project_relation = @project.project_members.new
end
def create
......@@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end
def update
@user_project_relation = project.project_members.find_by(user_id: member)
@user_project_relation = @project.project_members.find_by(user_id: member)
@user_project_relation.update_attributes(member_params)
unless @user_project_relation.valid?
......@@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end
def destroy
@user_project_relation = project.project_members.find_by(user_id: member)
@user_project_relation = @project.project_members.find_by(user_id: member)
@user_project_relation.destroy
respond_to do |format|
......@@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end
def leave
project.project_members.find_by(user_id: current_user).destroy
@project.project_members.find_by(user_id: current_user).destroy
respond_to do |format|
format.html { redirect_to :back }
......
......@@ -76,7 +76,7 @@ class ProjectsController < ApplicationController
end
def import
if project.import_finished?
if @project.import_finished?
redirect_to @project
return
end
......@@ -98,7 +98,7 @@ class ProjectsController < ApplicationController
end
def destroy
return access_denied! unless can?(current_user, :remove_project, project)
return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute
......@@ -148,8 +148,8 @@ class ProjectsController < ApplicationController
end
def archive
return access_denied! unless can?(current_user, :archive_project, project)
project.archive!
return access_denied! unless can?(current_user, :archive_project, @project)
@project.archive!
respond_to do |format|
format.html { redirect_to @project }
......@@ -157,8 +157,8 @@ class ProjectsController < ApplicationController
end
def unarchive
return access_denied! unless can?(current_user, :archive_project, project)
project.unarchive!
return access_denied! unless can?(current_user, :archive_project, @project)
@project.unarchive!
respond_to do |format|
format.html { redirect_to @project }
......
......@@ -80,7 +80,7 @@ class Note < ActiveRecord::Base
note_options = {
project: project,
author: author,
note: "_mentioned in #{gfm_reference}_",
note: cross_reference_note_content(gfm_reference),
system: true
}
......@@ -174,7 +174,7 @@ class Note < ActiveRecord::Base
where(noteable_id: noteable.id)
end
notes.where('note like ?', "_mentioned in #{gfm_reference}_").
notes.where('note like ?', cross_reference_note_content(gfm_reference)).
system.any?
end
......@@ -182,8 +182,16 @@ class Note < ActiveRecord::Base
where("note like :query", query: "%#{query}%")
end
def cross_reference_note_prefix
'_mentioned in '
end
private
def cross_reference_note_content(gfm_reference)
cross_reference_note_prefix + "#{gfm_reference}_"
end
# Prepend the mentioner's namespaced project path to the GFM reference for
# cross-project references. For same-project references, return the
# unmodified GFM reference.
......@@ -249,6 +257,10 @@ class Note < ActiveRecord::Base
nil
end
def cross_reference?
note.start_with?(self.class.cross_reference_note_prefix)
end
def find_diff
return nil unless noteable && noteable.diffs.present?
......
......@@ -119,7 +119,7 @@ class NotificationService
# ignore gitlab service messages
return true if note.note =~ /\A_Status changed to closed_/
return true if note.note =~ /\A_mentioned in / && note.system == true
return true if note.cross_reference? && note.system == true
opts = { noteable_type: note.noteable_type, project_id: note.project_id }
......
......@@ -2,39 +2,20 @@
- if @group.errors.any?
.alert.alert-danger
%span= @group.errors.full_messages.first
.form-group.group_name_holder
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Example Group", class: "form-control"
.form-group.group-description-holder
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
= render 'shared/group_form', f: f
.form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label'
.col-sm-10
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-group-avatar-input hidden"
.light The maximum file size allowed is 100KB.
= render 'shared/choose_group_avatar_button', f: f
- if @group.new_record?
.form-group
.col-sm-2
.col-sm-10
.bs-callout.bs-callout-info
%ul
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
= render 'shared/group_tips'
.form-actions
= f.submit 'Create group', class: "btn btn-create"
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
......
......@@ -11,16 +11,7 @@
- if @group.errors.any?
.alert.alert-danger
%span= @group.errors.full_messages.first
.form-group
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control left"
.form-group.group-description-holder
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
= render 'shared/group_form', f: f
.form-group
.col-sm-2
......@@ -31,13 +22,7 @@
You can change your group avatar here
- else
You can upload a group avatar here
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-group-avatar-input hidden"
.light The maximum file size allowed is 100KB.
= render 'shared/choose_group_avatar_button', f: f
- if @group.avatar?
%hr
= link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
......
......@@ -2,37 +2,18 @@
- if @group.errors.any?
.alert.alert-danger
%span= @group.errors.full_messages.first
.form-group
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control", tabindex: 1, autofocus: true
.form-group.group-description-holder
= f.label :description, "Details", class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4, tabindex: 2
= render 'shared/group_form', f: f, autofocus: true
.form-group.group-description-holder
= f.label :avatar, "Group avatar", class: 'control-label'
.col-sm-10
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-group-avatar-input hidden"
.light The maximum file size allowed is 100KB.
= render 'shared/choose_group_avatar_button', f: f
.form-group
.col-sm-2
.col-sm-10
%ul
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
= render 'shared/group_tips'
.form-actions
= f.submit 'Create group', class: "btn btn-create", tabindex: 3
%a.choose-btn.btn.btn-small.js-choose-group-avatar-button
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: 'js-group-avatar-input hidden'
.light The maximum file size allowed is 100KB.
.form-group
= f.label :name, class: 'control-label' do
Group name
.col-sm-10
= f.text_field :name, placeholder: 'Example Group', class: 'form-control',
autofocus: local_assigns[:autofocus] || false
.form-group.group-description-holder
= f.label :description, 'Details', class: 'control-label'
.col-sm-10
= f.text_area :description, maxlength: 250,
class: 'form-control js-gfm-input', rows: 4
%ul
%li A group is a collection of several projects
%li Groups are private by default
%li Members of a group may only view projects they have permission to access
%li Group project URLs are prefixed with the group namespace
%li Existing projects may be moved into a group
......@@ -11,7 +11,7 @@ namespace :gitlab do
home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Settings.gitlab.user_home
gitlab_url = Settings.gitlab.url
# gitlab-shell requires a / at the end of the url
gitlab_url += "/" unless gitlab_url.match(/\/$/)
gitlab_url += '/' unless gitlab_url.end_with?('/')
repos_path = Gitlab.config.gitlab_shell.repos_path
target_dir = Gitlab.config.gitlab_shell.path
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment