Commit f949245a authored by Douwe Maan's avatar Douwe Maan

Merge remote-tracking branch 'gitlab-ce/master' into ce-to-ee

parents 4af98170 bdb4288a
Please view this file on the master branch, on stable branches it's out of date.
v 7.14.0 (unreleased)
- Fix multi-line syntax highlighting (Stan Hu)
- Fix network graph when branch name has single quotes (Stan Hu)
- Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
- Add support for Unicode filenames in relative links (Hiroyuki Sato)
- Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
- Fix commit data retrieval when branch name has single quotes (Stan Hu)
- Check that project was actually created rather than just validated in import:repos task (Stan Hu)
- Fix full screen mode for snippet comments (Daniel Gerhardt)
- Fix 404 error in files view after deleting the last file in a repository (Stan Hu)
- Fix the "Reload with full diff" URL button (Stan Hu)
- Fix label read access for unauthenticated users (Daniel Gerhardt)
- Fix access to disabled features for unauthenticated users (Daniel Gerhardt)
- Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu)
......@@ -10,17 +18,40 @@ v 7.14.0 (unreleased)
- Set OmniAuth full_host parameter to ensure redirect URIs are correct (Stan Hu)
- Expire Rails cache entries after two weeks to prevent endless Redis growth
- Add support for destroying project milestones (Stan Hu)
- Add fetch command to the MR page
- Add project star and fork count, group avatar URL and user/group web URL attributes to API
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
- Add fetch command to the MR page.
- Fix bug causing "Remove source-branch" option not to work for merge requests from the same project.
- Fix approvals for forks. Now you can change approvals settings on the new merge request form
- Suggested approvers are shown on the new merge request form
- Disabled autocapitalize and autocorrect on login field (Daryl Chan)
- Mention group and project name in creation, update and deletion notices (Achilleas Pipinellis)
v 7.13.2
- Fix randomly failed spec
- Create project services on Project creation
- Add admin_merge_request ability to Developer level and up
- Fix Error 500 when browsing projects with no HEAD (Stan Hu)
- Fix labels / assignee / milestone for the merge requests when issues are disabled
- Show the first tab automatically on MergeRequests#new
- Add rake task 'gitlab:update_commit_count' (Daniel Gerhardt)
- Fix Gmail Actions
v 7.13.1
- Revert issue caching
- Reverted cache for events
- Fix: Label modifications are not reflected in existing notes and in the issue list
- Fix: Label not shown in the Issue list, although it's set through web interface
- Fix: Group/project references are linked incorrectly
- Improve documentation
- Fix of migration: Check if session_expire_delay column exists before adding the column
- Fix: ActionView::Template::Error
- Fix: "Create Merge Request" isn't always shown in event for newly pushed branch
- Fix bug causing "Remove source-branch" option not to work for merge requests from the same project.
- Render Note field hints consistently for "new" and "edit" forms
v 7.13.0 (unreleased)
v 7.13.0
- Remove repository graph log to fix slow cache updates after push event (Stan Hu)
- Return comments in created order in merge request API (Stan Hu)
- Only enable HSTS header for HTTPS and port 443 (Stan Hu)
- Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu)
- Fix redirection to home page URL for unauthorized users (Daniel Gerhardt)
......@@ -74,6 +105,7 @@ v 7.12.2
- Faster automerge check and merge itself when source and target branches are in same repository
- Audit log for user authentication
- Fix transferring of project to another group using the API.
- Allow custom label to be set for authentication providers.
v 7.12.1
- Fix error when deleting a user who has projects (Stan Hu)
......
......@@ -38,7 +38,7 @@ gem "browser", '~> 0.8.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.5'
gem "gitlab_git", '~> 7.2.6'
# Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
......@@ -275,4 +275,3 @@ end
gem "newrelic_rpm"
gem 'octokit', '3.7.0'
gem "rugments", "~> 1.0.0.beta8"
......@@ -272,7 +272,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
gitlab_git (7.2.5)
gitlab_git (7.2.6)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
......@@ -289,7 +289,7 @@ GEM
github-markup (~> 1.3.1)
gollum-grit_adapter (~> 0.1, >= 0.1.1)
nokogiri (~> 1.6.4)
rouge (~> 1.7.4)
rouge (~> 1.9)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
gon (5.0.1)
......@@ -537,7 +537,7 @@ GEM
netrc (~> 0.7)
rinku (1.7.3)
rotp (1.6.1)
rouge (1.7.7)
rouge (1.9.1)
rqrcode (0.4.2)
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
......@@ -580,7 +580,6 @@ GEM
rubyntlm (0.5.0)
rubypants (0.2.0)
rugged (0.22.2)
rugments (1.0.0.beta8)
safe_yaml (1.0.4)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
......@@ -786,7 +785,7 @@ DEPENDENCIES
gitlab-license (~> 0.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.5)
gitlab_git (~> 7.2.6)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
......@@ -839,7 +838,6 @@ DEPENDENCIES
rqrcode-rails3
rspec-rails (~> 3.3.0)
rubocop (= 0.28.0)
rugments (~> 1.0.0.beta8)
sanitize (~> 2.0)
sass-rails (~> 4.0.5)
sdoc
......
......@@ -166,9 +166,10 @@ $ ->
$('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff
$(".diff-content").on "click", ".supp_diff_link", ->
$(@).next('table').show()
$(@).remove()
$(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
$container = $(@).parent()
$container.next('table').show()
$container.remove()
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
......
......@@ -31,6 +31,10 @@ class @Diff
bottom: unfoldBottom
offset: offset
unfold: unfold
# indent is used to compensate for single space indent to fit
# '+' and '-' prepended to diff lines,
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1
$.get(link, params, (response) =>
target.parent().replaceWith(response)
......
......@@ -49,12 +49,6 @@ class @MergeRequestTabs
# Store the `location` object, allowing for easier stubbing in tests
@_location = location
switch @opts.action
when 'commits'
@commitsLoaded = true
when 'diffs'
@diffsLoaded = true
@bindEvents()
@activateTab(@opts.action)
......@@ -102,7 +96,7 @@ class @MergeRequestTabs
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)\/?$/, '')
new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
......@@ -133,7 +127,7 @@ class @MergeRequestTabs
return if @diffsLoaded
@_get
url: "#{source}.json"
url: "#{source}.json" + @_location.search
success: (data) =>
document.getElementById('diffs').innerHTML = data.html
@diffsLoaded = true
......
......@@ -10,7 +10,6 @@ class @Notes
constructor: (notes_url, note_ids, last_fetched_at, view) ->
@notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids
@last_fetched_at = last_fetched_at
@view = view
......
......@@ -184,7 +184,7 @@ li.note {
}
}
.supp_diff_link,
.show-suppressed-diff,
.show-all-commits {
cursor: pointer;
}
......
......@@ -65,6 +65,17 @@
color: #777;
}
.suppressed-container {
padding: ($padding-base-vertical + 5px) $padding-base-horizontal;
text-align: center;
// "Changes suppressed. Click to show." link
.show-suppressed-diff {
font-size: 110%;
font-weight: bold;
}
}
table {
width: 100%;
font-family: $monospace_font;
......
......@@ -37,7 +37,7 @@ ul.notes {
font-size: 13px;
a {
@extend .cgray;
@extend .cgray;
&:hover {
text-decoration: underline;
......@@ -105,6 +105,8 @@ ul.notes {
}
hr {
// Darken 'whitesmoke' a bit to make it more visible in note bodies
border-color: darken(#F5F5F5, 8%);
margin: 10px 0;
}
}
......
......@@ -305,14 +305,14 @@ class ApplicationController < ActionController::Base
end
def github_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:github)
Gitlab::OAuth::Provider.enabled?(:github)
end
def gitlab_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:gitlab)
Gitlab::OAuth::Provider.enabled?(:gitlab)
end
def bitbucket_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
end
end
......@@ -3,6 +3,10 @@ class Groups::ApplicationController < ApplicationController
private
def group
@group ||= Group.find_by(path: params[:group_id])
end
def authorize_read_group!
unless @group and can?(current_user, :read_group, @group)
if current_user.nil?
......@@ -19,7 +23,9 @@ class Groups::ApplicationController < ApplicationController
end
end
def group
@group ||= Group.find_by(path: params[:group_id])
def authorize_admin_group_member!
unless can?(current_user, :admin_group_member, group)
return render_403
end
end
end
......@@ -5,6 +5,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_read_group!
before_action :authorize_admin_group!, except: [:index, :leave]
before_action :authorize_admin_group_member!, only: [:create, :resend_invite]
def index
@project = @group.projects.find(params[:project_id]) if params[:project_id]
......@@ -36,6 +37,9 @@ class Groups::GroupMembersController < Groups::ApplicationController
def update
@member = @group.group_members.find(params[:id])
return render_403 unless can?(current_user, :update_group_member, @member)
old_access_level = @member.human_access
if @member.update_attributes(member_params)
......
......@@ -24,7 +24,7 @@ class GroupsController < Groups::ApplicationController
if @group.save
@group.add_owner(current_user)
redirect_to @group, notice: 'Group was successfully created.'
redirect_to @group, notice: "Group '#{@group.name}' was successfully created."
else
render action: "new"
end
......@@ -77,7 +77,7 @@ class GroupsController < Groups::ApplicationController
def update
if @group.update_attributes(group_params)
redirect_to edit_group_path(@group), notice: 'Group was successfully updated.'
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
else
render action: "edit"
end
......@@ -86,7 +86,7 @@ class GroupsController < Groups::ApplicationController
def destroy
DestroyGroupService.new(@group, current_user).execute
redirect_to root_path, notice: 'Group was removed.'
redirect_to root_path, alert: "Group '#{@group.name} was deleted."
end
protected
......
......@@ -3,6 +3,7 @@ class Import::BitbucketController < Import::BaseController
before_action :bitbucket_auth, except: :callback
rescue_from OAuth::Error, with: :bitbucket_unauthorized
rescue_from Gitlab::BitbucketImport::Client::Unauthorized, with: :bitbucket_unauthorized
def callback
request_token = session.delete(:oauth_request_token)
......
......@@ -77,10 +77,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
end
rescue Gitlab::OAuth::SignupDisabledError => e
message = "Signing in using your #{oauth['provider']} account without a pre-existing GitLab account is not allowed."
label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."
if current_application_settings.signup_enabled?
message << " Create a GitLab account first, and then connect it to your #{oauth['provider']} account."
message << " Create a GitLab account first, and then connect it to your #{label} account."
end
flash[:notice] = message
......
......@@ -7,6 +7,10 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :authorize_download_code!
def show
@url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))
@commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")
respond_to do |format|
format.html
......
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
include TreeHelper
before_action :require_non_empty_project
before_action :assign_ref_vars
......@@ -60,6 +61,11 @@ class Projects::RefsController < Projects::ApplicationController
}
end
if @logs.present?
@log_url = namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))
@more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))
end
respond_to do |format|
format.html { render_404 }
format.js
......
......@@ -24,7 +24,7 @@ class ProjectsController < ApplicationController
if @project.saved?
redirect_to(
project_path(@project),
notice: 'Project was successfully created.'
notice: "Project '#{@project.name}' was successfully created."
)
else
render 'new'
......@@ -36,11 +36,11 @@ class ProjectsController < ApplicationController
respond_to do |format|
if status
flash[:notice] = 'Project was successfully updated.'
flash[:notice] = "Project '#{@project.name}' was successfully updated."
format.html do
redirect_to(
edit_project_path(@project),
notice: 'Project was successfully updated.'
notice: "Project '#{@project.name}' was successfully updated."
)
end
format.js
......@@ -100,7 +100,7 @@ class ProjectsController < ApplicationController
return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute
flash[:alert] = 'Project deleted.'
flash[:alert] = "Project '#{@project.name}' was deleted."
if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path
......
......@@ -90,7 +90,7 @@ class SessionsController < Devise::SessionsController
# Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil
redirect_to omniauth_authorize_path(:user, provider.to_sym)
redirect_to user_omniauth_authorize_path(provider.to_sym)
end
def valid_otp_attempt?(user)
......
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze
def ldap_enabled?
Gitlab.config.ldap.enabled
end
def kerberos_enabled?
enabled_oauth_providers.include?(:kerberos)
end
def provider_has_icon?(name)
PROVIDERS_WITH_ICONS.include?(name.to_s)
end
def auth_providers
Gitlab::OAuth::Provider.providers
end
def label_for_provider(name)
Gitlab::OAuth::Provider.label_for(name)
end
def form_based_provider?(name)
FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s }
end
def form_based_providers
auth_providers.select { |provider| form_based_provider?(provider) }
end
def button_based_providers
auth_providers.reject { |provider| form_based_provider?(provider) }
end
def provider_image_tag(provider, size = 64)
label = label_for_provider(provider)
if provider_has_icon?(provider)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}")
else
label
end
end
def auth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
extend self
end
module BlobHelper
def highlight(blob_name, blob_content, nowrap: false, continue: false)
@formatter ||= Rugments::Formatters::HTML.new(
@formatter ||= Rouge::Formatters::HTMLGitlab.new(
nowrap: nowrap,
cssclass: 'code highlight',
lineanchors: true,
......@@ -8,11 +8,11 @@ module BlobHelper
)
begin
@lexer ||= Rugments::Lexer.guess(filename: blob_name, source: blob_content).new
@lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
rescue
lexer = Rugments::Lexers::PlainText
result = @formatter.format(lexer.lex(blob_content)).html_safe
@lexer = Rouge::Lexers::PlainText
result = @formatter.format(@lexer.lex(blob_content)).html_safe
end
result
......
......@@ -31,8 +31,8 @@ module EmailsHelper
end
def color_email_diff(diffcontent)
formatter = Rugments::Formatters::HTML.new(cssclass: "highlight", inline_theme: :github)
lexer = Rugments::Lexers::Diff.new
formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github')
lexer = Rouge::Lexers::Diff
raw formatter.format(lexer.lex(diffcontent))
end
......
module OauthHelper
def ldap_enabled?
Gitlab.config.ldap.enabled
end
def kerberos_enabled?
enabled_oauth_providers.include?(:kerberos)
end
def standard_login_form_only?
ldap_enabled? || kerberos_enabled?
end
def default_providers
[:twitter, :github, :gitlab, :bitbucket, :google_oauth2, :ldap]
end
def enabled_oauth_providers
Devise.omniauth_providers
end
def enabled_social_providers
enabled_oauth_providers.select do |name|
[:saml, :twitter, :gitlab, :github, :bitbucket, :google_oauth2, :kerberos].include?(name.to_sym)
end
end
def additional_providers
enabled_oauth_providers.reject do |provider|
provider.to_s.starts_with?('ldap') || provider == :kerberos
end
end
def oauth_image_tag(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}")
end
def oauth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
extend self
end
module ProfileHelper
def show_profile_username_tab?
current_user.can_change_username?
end
def show_profile_social_tab?
enabled_social_providers.any?
end
def show_profile_remove_tab?
signup_enabled?
end
end
......@@ -286,7 +286,8 @@ module ProjectsHelper
end
def readme_cache_key
[@project.id, @project.commit.sha, "readme"].join('-')
sha = @project.commit.try(:sha) || 'nil'
[@project.id, sha, "readme"].join('-')
end
def round_commit_count(project)
......
......@@ -156,12 +156,13 @@ class Ability
:create_project_snippet,
:update_issue,
:admin_issue,
:admin_label,
:admin_label
]
end
def project_dev_rules
project_report_rules + [
:admin_merge_request,
:create_merge_request,
:create_wiki,
:push_code
......@@ -248,7 +249,8 @@ class Ability
if group.has_owner?(user) || user.admin?
rules.push(*[
:admin_group,
:admin_namespace
:admin_namespace,
:admin_group_member
])
end
......@@ -310,7 +312,7 @@ class Ability
rules = []
target_user = subject.user
group = subject.group
can_manage = group_abilities(user, group).include?(:admin_group)
can_manage = group_abilities(user, group).include?(:admin_group_member)
if can_manage && (user != target_user)
rules << :update_group_member
......
......@@ -159,6 +159,16 @@ module Issuable
end
end
# Convert this Issuable class name to a format usable by Ability definitions
#
# Examples:
#
# issuable.class # => MergeRequest
# issuable.to_ability_name # => "merge_request"
def to_ability_name
self.class.to_s.underscore
end
private
def filter_superceded_votes(votes, notes)
......
......@@ -60,6 +60,12 @@ class Group < Namespace
name
end
def avatar_url(size = nil)
if avatar.present?
[gitlab_config.url, avatar.url].join
end
end
def owners
@owners ||= group_members.owners.map(&:user)
end
......
......@@ -36,7 +36,6 @@ class Project < ActiveRecord::Base
include Gitlab::ConfigHelper
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
include Rails.application.routes.url_helpers
include Referable
include Sortable
......@@ -323,7 +322,7 @@ class Project < ActiveRecord::Base
end
def web_url
[gitlab_config.url, path_with_namespace].join('/')
Rails.application.routes.url_helpers.namespace_project_url(self.namespace, self)
end
def web_url_without_protocol
......@@ -448,7 +447,7 @@ class Project < ActiveRecord::Base
if avatar.present?
[gitlab_config.url, avatar.url].join
elsif avatar_in_git
[gitlab_config.url, namespace_project_avatar_path(namespace, self)].join
Rails.application.routes.url_helpers.namespace_project_avatar_url(namespace, self)
end
end
......@@ -591,7 +590,7 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo
[gitlab_config.url, '/', path_with_namespace, '.git'].join('')
"#{web_url}.git"
end
# Check if current branch name is marked as protected in the system
......@@ -733,14 +732,14 @@ class Project < ActiveRecord::Base
ensure_satellite_exists
true
else
errors.add(:base, 'Failed to fork repository')
errors.add(:base, 'Failed to fork repository via gitlab-shell')
false
end
else
if gitlab_shell.add_repository(path_with_namespace)
true
else
errors.add(:base, 'Failed to create repository')
errors.add(:base, 'Failed to create repository via gitlab-shell')
false
end
end
......
......@@ -281,6 +281,10 @@ class User < ActiveRecord::Base
value: login.to_s.downcase).first
end
def find_by_username!(username)
find_by!('lower(username) = ?', username.downcase)
end
def by_username_or_id(name_or_id)
where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
end
......
......@@ -27,8 +27,10 @@ class IssuableBaseService < BaseService
old_branch, new_branch)
end
def filter_params
unless can?(current_user, :admin_issue, project)
def filter_params(issuable_ability_name = :issue)
ability = :"admin_#{issuable_ability_name}"
unless can?(current_user, ability, project)
params.delete(:milestone_id)
params.delete(:label_ids)
params.delete(:assignee_id)
......
......@@ -10,6 +10,10 @@ module Issues
private
def filter_params
super(:issue)
end
def execute_hooks(issue, action = 'open')
issue_data = hook_data(issue, action)
issue.project.execute_hooks(issue_data, :issue_hooks)
......
......@@ -20,5 +20,11 @@ module MergeRequests
merge_request.project.execute_services(merge_data, :merge_request_hooks)
end
end
private
def filter_params
super(:merge_request)
end
end
end
......@@ -85,6 +85,8 @@ module Projects
@project.create_wiki if @project.wiki_enabled?
@project.build_missing_services
event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create)
......
......@@ -78,21 +78,22 @@
%span.monospace= project.path_with_namespace + ".git"
.col-md-6
.panel.panel-default
.panel-heading
Add user(s) to the group:
.panel-body.form-holder
%p.light
Read more about project permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
- if can?(current_user, :admin_group_member, @group)
.panel.panel-default
.panel-heading
Add user(s) to the group:
.panel-body.form-holder
%p.light
Read more about project permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
= form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div
= users_select_tag(:user_ids, multiple: true, email_user: true, skip_ldap: @group.ldap_synced?, scope: :all)
%div.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
= button_tag 'Add users to group', class: "btn btn-create"
= form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div
= users_select_tag(:user_ids, multiple: true, email_user: true, skip_ldap: @group.ldap_synced?, scope: :all)
%div.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
= button_tag 'Add users to group', class: "btn btn-create"
.panel.panel-default
.panel-heading
%h3.panel-title
......@@ -113,7 +114,8 @@
(invited)
%span.pull-right.light
= member.human_access
= link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse
- if can?(current_user, :destroy_group_member, member)
= link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse
.panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab'
......@@ -8,7 +8,8 @@
.form-group
= f.label :provider, class: 'control-label'
.col-sm-10
= f.select :provider, Gitlab::OAuth::Provider.names, { allow_blank: false }, class: 'form-control'
- values = Gitlab::OAuth::Provider.providers.map { |name| ["#{Gitlab::OAuth::Provider.label_for(name)} (#{name})", name] }
= f.select :provider, values, { allow_blank: false }, class: 'form-control'
.form-group
= f.label :extern_uid, "Identifier", class: 'control-label'
.col-sm-10
......
%tr
%td
= identity.provider
= "#{Gitlab::OAuth::Provider.label_for(identity.provider)} (#{identity.provider})"
%td
= identity.extern_uid
%td
......
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
= f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus"
= f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off"
= f.password_field :password, class: "form-control bottom", placeholder: "Password"
- if devise_mapping.rememberable?
.remember-me.checkbox
......
......@@ -6,4 +6,4 @@
%label{for: "remember_me"}
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
= button_tag "#{server['label']} Sign in", class: "btn-save btn"
= button_tag "Sign in", class: "btn-save btn"
%p
%span.light
Sign in with &nbsp;
- providers = additional_providers
- providers = button_based_providers
- providers.each do |provider|
%span.light
- if default_providers.include?(provider)
= link_to oauth_image_tag(provider), omniauth_authorize_path(resource_name, provider), method: :post, class: 'oauth-image-link'
- else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), method: :post, class: "btn", "data-no-turbolink" => "true"
- has_icon = provider_has_icon?(provider)
= link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
......@@ -6,7 +6,7 @@
.login-heading
%h3 Sign in
.login-body
- if ldap_enabled?
- if form_based_providers.any?
%ul.nav.nav-tabs
- @ldap_servers.each_with_index do |server, i|
%li{class: (:active if i.zero?)}
......
......@@ -24,7 +24,7 @@
= link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at)
- if show_controls && can?(current_user, :admin_group, @group)
- if show_controls && can?(current_user, :admin_group_member, member)
= link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
Resend invite
......
......@@ -17,7 +17,7 @@
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
= button_tag 'Search', class: 'btn'
- if current_user && current_user.can?(:admin_group, @group)
- if current_user && current_user.can?(:admin_group_member, @group)
.pull-right
- if ldap_enabled? && @group.ldap_group_links.any?
= link_to reset_access_group_ldap_path(@group), class: 'btn btn-grouped', data: { confirm: "Force GitLab to do LDAP permission checks for all group members? All members besides yourself will be reduced to 'Guest' access until their next interaction with GitLab." }, method: :put do
......
......@@ -33,7 +33,7 @@
= yield
%div.footer{style: "margin-top: 10px;"}
%p
\—
&mdash;
%br
- if @target_url
#{link_to "View it on GitLab", @target_url}
......
......@@ -58,22 +58,22 @@
%div
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab?
- if button_based_providers.any?
.panel.panel-default
.panel-heading
Connected Accounts
.panel-body
.oauth-buttons.append-bottom-10
%p Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider|
- button_based_providers.each do |provider|
.btn-group
= link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider),
method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- if oauth_active?(provider)
= link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: "btn btn-lg #{'active' if auth_active?(provider)}", "data-no-turbolink" => "true"
- if auth_active?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
= icon('close')
- if show_profile_username_tab?
- if current_user.can_change_username?
.panel.panel-warning.update-username
.panel-heading
Change Username
......@@ -93,7 +93,7 @@
%div
= f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab?
- if signup_enabled?
.panel.panel-danger.remove-account
.panel-heading
Remove account
......
......@@ -6,14 +6,13 @@
- @key.errors.full_messages.each do |msg|
%li= msg
.form-group
= f.label :title, class: 'control-label'
.col-sm-10= f.text_field :title, class: "form-control"
.form-group
= f.label :key, class: 'control-label'
.col-sm-10
= f.text_area :key, class: "form-control", rows: 8
.form-group
= f.label :title, class: 'control-label'
.col-sm-10= f.text_field :title, class: "form-control"
.form-actions
= f.submit 'Add key', class: "btn btn-create"
......
......@@ -11,7 +11,7 @@
%td.old_line.diff-line-num{data: {linenumber: line_old}}
= link_to raw(line_old), "#"
%td.new_line= link_to raw(line_new) , "#"
%td.line_content.noteable_line= line
%td.line_content.noteable_line= ' ' * @form.indent + line
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc
%tr.line_holder{ id: @form.to }
......
- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES
- if too_big
%a.supp_diff_link Changes suppressed. Click to show
.suppressed-container
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%table.text-file{class: "#{'hide' if too_big}"}
- last_line = 0
......
......@@ -3,7 +3,7 @@
Too many changes to show.
.pull-right
- unless diff_hard_limit_enabled?
= link_to "Reload with full diff", url_for(params.merge(force_show_diff: true)), class: "btn btn-sm btn-warning"
= link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: :html)), class: "btn btn-sm btn-warning"
- if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit)
......
......@@ -66,11 +66,9 @@
#notes.notes.tab-pane.voting_notes
= render "projects/merge_requests/discussion"
#commits.commits.tab-pane
- if current_page?(action: 'commits')
= render "projects/merge_requests/show/commits"
- # This tab is always loaded via AJAX
#diffs.diffs.tab-pane
- if current_page?(action: 'diffs')
= render "projects/merge_requests/show/diffs"
- # This tab is always loaded via AJAX
.mr-loading-status
= spinner
......
......@@ -17,9 +17,9 @@
:javascript
network_graph = new Network({
url: '#{namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))}',
commit_url: '#{namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")}',
ref: '#{@ref}',
url: "#{escape_javascript(@url)}",
commit_url: "#{escape_javascript(@commit_url)}",
ref: "#{escape_javascript(@ref)}",
commit_id: '#{@commit.id}'
})
new ShortcutsNetwork(network_graph.branch_graph)
......@@ -3,10 +3,7 @@
= note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
.comment-hints.clearfix
.pull-left #{link_to 'Markdown ', help_page_path('markdown', 'markdown'),{ target: '_blank', tabindex: -1 }}
.pull-right #{link_to 'Attach a file', '#', class: 'markdown-selector', tabindex: -1 }
= render 'projects/notes/hints'
.note-form-actions
.buttons
......
......@@ -8,18 +8,8 @@
= f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text'
.comment-hints.clearfix
.pull-left
= link_to "Markdown ", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }
tip:
= random_markdown_tip
.pull-right
= link_to '#', class: 'markdown-selector', tabindex: -1 do
Attach a file
= icon('paperclip')
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text'
= render 'projects/notes/hints'
.error-alert
.note-form-actions
......
.comment-hints.clearfix
.pull-left
= link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1
tip:
= random_markdown_tip
.pull-right
= link_to '#', class: 'markdown-selector', tabindex: -1 do
= icon('paperclip')
Attach a file
......@@ -11,9 +11,11 @@
- if @logs.present?
:plain
var current_url = location.href.replace(/\/?$/, '/');
var log_url = '#{namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/');
var log_url = "#{escape_javascript(@log_url)}".replace(/\/?$/, '/');
if(current_url == log_url) {
// Load 10 more commit log for each file in tree
// Load more commit logs for each file in tree
// if we still on the same page
ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))}');
var url = "#{escape_javascript(@more_log_url)}";
ajaxGet(url);
}
......@@ -49,5 +49,5 @@
:javascript
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}');
ajaxGet("#{escape_javascript(@logs_path)}");
});
......@@ -3,6 +3,10 @@
%h3.page-title
= @page.title
= render 'main_links'
.wiki-last-edit-by
Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
- if @page.historical?
.warning_message
This is an old version of this page.
......@@ -16,6 +20,6 @@
= render_wiki_content(@page)
%hr
.wiki-last-edit-by
Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
......@@ -8,7 +8,7 @@
- else
none
.issuable-context-selectbox
- if can?(current_user, :"admin_#{issuable.class.to_s.underscore}", @project)
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true)
%div.prepend-top-20.clearfix
......@@ -24,7 +24,7 @@
- else
none
.issuable-context-selectbox
- if can?(current_user, :"admin_#{issuable.class.to_s.underscore}", @project)
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :issuable_context
= f.submit class: 'btn hide'
......
......@@ -38,7 +38,7 @@
.clearfix
.error-alert
%hr
- if can?(current_user, :admin_issue, @project)
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.form-group
.issue-assignee
= f.label :assignee_id, class: 'control-label' do
......
......@@ -243,24 +243,30 @@ production: &base
# arguments, followed by optional 'args' which can be either a hash or an array.
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
providers:
# - { name: 'google_oauth2', app_id: 'YOUR_APP_ID',
# - { name: 'google_oauth2',
# label: 'Google',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET',
# args: { access_type: 'offline', approval_prompt: '' } }
# - { name: 'twitter', app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET'}
# - { name: 'gitlab', app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'api' } }
# - { name: 'github', app_id: 'YOUR_APP_ID',
# - { name: 'twitter',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET' }
# - { name: 'github',
# label: 'GitHub',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET',
# url: "https://github.com/",
# args: { scope: 'user:email' } }
# - { name: 'gitlab', app_id: 'YOUR_APP_ID',
# - { name: 'gitlab',
# label: 'GitLab.com',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'api' } }
# - { name: 'bitbucket', app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET'}
# - { name: 'saml',
# - { name: 'bitbucket',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET' }
# - { name: 'saml',
# label: 'Our SAML Provider',
# args: {
# assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
# idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
......
......@@ -2,7 +2,7 @@
Step-by-step guides on the basics of working with Git and GitLab.
* [Start using Git on the commandline](start-using-git.md)
* [Start using Git on the command line](start-using-git.md)
* [Create and add your SSH Keys](create-your-ssh-keys.md)
......@@ -17,3 +17,5 @@ Step-by-step guides on the basics of working with Git and GitLab.
* [Create a branch](create-branch.md)
* [Fork a project](fork-project.md)
* [Add a file](add-file.md)
# How to add a file
You can create a file in your [shell](command-line-commands.md) or in GitLab.
To create a file in GitLab, sign in to [GitLab.com](https://gitlab.com).
Select a project on the right side of your screen:
![Select a project](basicsimages/select_project.png)
It's a good idea to [create a branch](create-branch.md), but it's not necessary.
Go to the directory where you'd like to add the file and click on the "+" sign next to the name of the project and directory:
![Create a file](basicsimages/create_file.png)
Name your file (you can't add spaces, so you can use hyphens or underscores). Don't forget to include the markup language you'd like to use :
![File name](basicsimages/file_name.png)
Add all the information that you'd like to include in your file:
![Add information](basicsimages/white_space.png)
Add a commit message based on what you just added and then click on "commit changes":
![Commit changes](basicsimages/commit_changes.png)
### Note
Besides its regular files, every directory needs a README.md or README.html file which works like an index, telling
what the directory is about. It's the first document you'll find when you open a directory.
# GitLab buttons in Gmail
GitLab supports [Google actions in email](https://developers.google.com/gmail/markup/actions/actions-overview).
If correctly setup, emails that require an action will be marked in Gmail.
![gitlab_actions](gitlab_actions.png)
To get this functioning, you need to be registered with Google.
[See how to register with Google in this document.](https://developers.google.com/gmail/markup/registering-with-google)
To aid the registering with Google, GitLab offers a rake task that will send an email to Google whitelisting email address from your GitLab server.
To check what would be sent to the Google email address, run the rake task:
```bash
bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production
```
**This will not send the email but give you the output of how the mail will look.**
Copy the output of the rake task to [Google email markup tester](https://www.google.com/webmasters/markup-tester/u/0/) and press "Validate".
If you receive "No errors detected" message from the tester you can send the email using:
```bash
bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production SEND=true
```
......@@ -84,7 +84,7 @@ Existing users can enable OmniAuth for specific providers after the account is c
1. Sign in normally - whether standard sign in, LDAP, or another OmniAuth provider.
1. Go to profile settings (the silhouette icon in the top right corner).
1. Select the "Account" tab.
1. Under "Social Accounts" select the desired OmniAuth provider, such as Twitter.
1. Under "Connected Accounts" select the desired OmniAuth provider, such as Twitter.
1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab.
The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on.
......
......@@ -2,9 +2,7 @@
To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter will generate a client ID and secret key for you to use.
1. Sign in to [Twitter Developers](https://dev.twitter.com/) area.
1. Hover over the avatar in the top right corner and select "My applications."
1. Sign in to [Twitter Application Management](https://apps.twitter.com/).
1. Select "Create new app"
......@@ -14,18 +12,18 @@ To enable the Twitter OmniAuth provider you must register your application with
- Description: Create a description.
- Website: The URL to your GitLab installation. 'https://gitlab.example.com'
- Callback URL: 'https://gitlab.example.com/users/auth/twitter/callback'
- Agree to the "Rules of the Road."
- Agree to the "Developer Agreement".
![Twitter App Details](twitter_app_details.png)
1. Select "Create your Twitter application."
1. Select the "Settings" tab.
1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in the Twitter."
1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in with Twitter."
1. Select "Update settings" at the bottom to save changes.
1. Select the "API Keys" tab.
1. Select the "Keys and Access Tokens" tab.
1. You should now see an API key and API secret (see screenshot). Keep this page open as you continue configuration.
......@@ -78,4 +76,4 @@ To enable the Twitter OmniAuth provider you must register your application with
1. Restart GitLab for the changes to take effect.
On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
\ No newline at end of file
......@@ -17,6 +17,7 @@ If a user is a GitLab administrator they receive all permissions.
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
| Manage merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
......
......@@ -68,7 +68,7 @@ Xth: (2 working days before the 22nd)
Xth: (1 working day before the 22nd)
- [ ] Merge CE stable into EE stable
- [ ] Create (hopefully final) CE, EE, CI release candidates (#LINK)
- [ ] Create CE, EE, CI release candidates (#LINK) (hopefully final ones with the same commit as the release tomorrow)
- [ ] Create Omnibus tags and build packages for the latest release candidates
- [ ] Update GitLab.com with the latest RC (#LINK)
- [ ] Update ci.gitLab.com with the latest RC (#LINK)
......@@ -81,10 +81,10 @@ workday to quickly fix any issues.
- [ ] Merge CE stable into EE stable (#LINK)
- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK)
- [ ] BEFORE 11AM CET Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK)
- [ ] BEFORE 12AM CET Publish the release blog post (#LINK)
- [ ] Try to do before 11AM CET: Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK)
- [ ] Try to do before 12AM CET: Publish the release blog post (#LINK)
- [ ] Tweet about the release (blog post) (#LINK)
- [ ] Schedule a second tweet of the release announcement at 6PM CET / 9AM PST
- [ ] Schedule a second tweet of the release announcement with the same text at 6PM CET / 9AM PST
```
......@@ -220,4 +220,4 @@ Consider creating a post on Hacker News.
## Create a WIP blogpost for the next release
Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md).
Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md).
\ No newline at end of file
......@@ -105,3 +105,6 @@ IdentityFile ~/my-ssh-key-directory/company-com-private-key-filename
Note in the gitlab.com example above a username was specified to override the default chosen by OpenSSH (your local username). This is only required if your local and remote usernames differ.
Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.
Public SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you'll
have when pushing code via SSH. That's why it needs to uniquely map to a single user.
......@@ -31,7 +31,7 @@ We think there is still room for improvement and will detail a set of practices
## Git flow and its problems
[![Git Flow timeline by Vincent Driessen, used with permission](gitdashflow.png)
![Git Flow timeline by Vincent Driessen, used with permission](gitdashflow.png)
Git flow was one of the first proposals to use git branches and it has gotten a lot of attention.
It advocates a master branch and a separate develop branch as well as supporting branches for features, releases and hotfixes.
......@@ -54,7 +54,7 @@ And doing releases doesn't automatically mean also doing hotfixes.
![Master branch with feature branches merged in](github_flow.png)
In reaction to git flow a simpler alternative was detailed, [GitHub flow](https://guides.github.com/introduction/flow/index.html).
In reaction to git flow a simpler alternative was detailed, [GitHub flow](https://guides.github.com/introduction/flow/index.html).
This flow has only feature branches and a master branch.
This is very simple and clean, many organizations have adopted it with great success.
Atlassian recommends [a similar strategy](http://blogs.atlassian.com/2014/01/simple-git-workflow-simple/) although they rebase feature branches.
......@@ -131,7 +131,7 @@ When you feel comfortable with it to be merged you assign it to the person that
There is room for more feedback and after the assigned person feels comfortable with the result the branch is merged.
If the assigned person does not feel comfortable they can close the merge request without merging.
In GitLab it is common to protect the long-lived branches (e.g. the master branch) so that normal developers [can't modify these protected branches](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/permissions/permissions.md).
In GitLab it is common to protect the long-lived branches (e.g. the master branch) so that normal developers [can't modify these protected branches](http://doc.gitlab.com/ce/permissions/permissions.html).
So if you want to merge it into a protected branch you assign it to someone with master authorizations.
## Issues with GitLab flow
......@@ -216,7 +216,7 @@ This prevents creating a merge commit when merging master into your feature bran
However, just like with squashing you should never rebase commits you have pushed to a remote server.
This makes it impossible to rebase work in progress that you already shared with your team which is something we recommend.
When using rebase to keep your feature branch updated you [need to resolve similar conflicts again and again](http://blogs.atlassian.com/2013/10/git-team-workflows-merge-or-rebase/).
You can reuse recorded resolutions (rerere) sometimes, but with without rebasing you only have to solve the conflicts one time and you’re set.
You can reuse recorded resolutions (rerere) sometimes, but without rebasing you only have to solve the conflicts one time and you’re set.
There has to be a better way to avoid many merge commits.
The way to prevent creating many merge commits is to not frequently merge master into the feature branch.
......
......@@ -6,4 +6,7 @@
4. [SVN](migrating_from_svn.md)
### Note
* If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported.
\ No newline at end of file
* If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported.
* Repositories are imported to GitLab via HTTP.
If the repository is too large, it can timeout. We have a soft limit of 10GB.
# Labels
In GitLab, you can easily tag issues and merge requests. If you have permission level `Developer` or higher, you can manage labels. To create, edit or delete a label, go to a project and then to `Issues` and then `Labels`.
In GitLab, you can easily tag issues and Merge Requests. If you have permission level `Developer` or higher, you can manage labels. To create, edit or delete a label, go to a project and then to `Issues` and then `Labels`.
Here you can create a new label.
......@@ -14,3 +14,5 @@ If you want to change an existing label, press edit next to the listed label.
You will be presented with the same form as when creating a new label.
![edit label](labels/label3.png)
You can add labels to Merge Requests when you create or edit them.
......@@ -10,6 +10,11 @@ Feature: Project Network Graph
And page should select "master" in select box
And page should have "master" on graph
@javascript
Scenario: I should see project network with 'test' branch
When I visit project network page on branch 'test'
Then page should have 'test' on graph
@javascript
Scenario: I should switch "branch" and "tag"
When I switch ref to "feature"
......
......@@ -158,3 +158,10 @@ Feature: Project Source Browse Files
Given I visit project source page for "6d394385cf567f80a8fd85055db1ab4c5295806f"
And I click on ".gitignore" file in repo
Then I don't see the permalink link
@javascript
Scenario: I browse code with single quotes in the ref
Given I switch ref to 'test'
And I see the ref 'test' has been selected
And I visit the 'test' tree
Then I see the commit data
......@@ -3,6 +3,14 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
include SharedPaths
include SharedAdmin
before do
allow(Devise).to receive(:omniauth_providers).and_return([:twitter, :twitter_updated])
end
after do
allow(Devise).to receive(:omniauth_providers).and_call_original
end
step 'I should see all users' do
User.all.each do |user|
expect(page).to have_content user.name
......@@ -121,7 +129,6 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I visit "Pete" identities page in admin' do
allow(Gitlab::OAuth::Provider).to receive(:names).and_return(%w(twitter twitter_updated))
visit admin_user_identities_path(@user)
end
......
......@@ -11,8 +11,12 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
# Stub Graph max_size to speed up test (10 commits vs. 650)
allow(Network::Graph).to receive(:max_count).and_return(10)
project = Project.find_by(name: "Shop")
visit namespace_project_network_path(project.namespace, project, "master")
@project = Project.find_by(name: "Shop")
visit namespace_project_network_path(@project.namespace, @project, "master")
end
step "I visit project network page on branch 'test'" do
visit namespace_project_network_path(@project.namespace, @project, "'test'")
end
step 'page should select "master" in select box' do
......@@ -29,6 +33,12 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
end
end
step "page should have 'test' on graph" do
page.within '.network-graph' do
expect(page).to have_content "'test'"
end
end
When 'I switch ref to "feature"' do
select 'feature', from: 'ref'
sleep 2
......
......@@ -193,6 +193,23 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
FileUtils.rm_f(File.join(@project.repository.path, 'hooks', 'pre-receive'))
end
step "I switch ref to 'test'" do
select "'test'", from: 'ref'
end
step "I see the ref 'test' has been selected" do
expect(page).to have_selector '.select2-chosen', text: "'test'"
end
step "I visit the 'test' tree" do
visit namespace_project_tree_path(@project.namespace, @project, "'test'")
end
step 'I see the commit data' do
expect(page).to have_css('.tree-commit-link', visible: true)
expect(page).not_to have_content('Loading commit data...')
end
private
def set_new_content
......
......@@ -6,6 +6,10 @@ module API
class UserBasic < UserSafe
expose :id, :state, :avatar_url
expose :web_url do |user, options|
Rails.application.routes.url_helpers.user_url(user)
end
end
class User < UserBasic
......@@ -64,6 +68,7 @@ module API
expose :namespace
expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
expose :avatar_url
expose :star_count, :forks_count
end
class ProjectMember < UserBasic
......@@ -79,6 +84,11 @@ module API
class Group < Grape::Entity
expose :id, :name, :path, :ldap_cn, :ldap_access, :description
expose :ldap_group_links, using: Entities::LdapGroupLink, if: lambda{ | group, options | group.ldap_group_links.any? }
expose :avatar_url
expose :web_url do |group, options|
Rails.application.routes.url_helpers.group_url(group)
end
end
class GroupDetail < Group
......
......@@ -229,7 +229,7 @@ module API
authorize! :read_merge_request, merge_request
present paginate(merge_request.notes), with: Entities::MRNote
present paginate(merge_request.notes.fresh), with: Entities::MRNote
end
# Post comment to merge request
......
......@@ -7,7 +7,11 @@ module Backup
def initialize
@config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
@db_dir = File.join(Gitlab.config.backup.path, 'db')
FileUtils.mkdir_p(@db_dir) unless Dir.exists?(@db_dir)
FileUtils.rm_rf(@db_dir)
# Ensure the parent dir of @db_dir exists
FileUtils.mkdir_p(Gitlab.config.backup.path)
# Fail if somebody raced to create @db_dir before us
FileUtils.mkdir(@db_dir, mode: 0700)
end
def dump
......@@ -25,7 +29,6 @@ module Backup
abort 'Backup failed' unless success
$progress.print 'Compressing database ... '
FileUtils.rm_f db_file_name_gz
success = system('gzip', db_file_name)
report_success(success)
abort 'Backup failed: compress error' unless success
......
......@@ -16,8 +16,6 @@ module Backup
file << s.to_yaml.gsub(/^---\n/,'')
end
FileUtils.chmod(0700, folders_to_backup)
# create archive
$progress.print "Creating backup archive: #{tar_file} ... "
orig_umask = File.umask(0077)
......
......@@ -130,7 +130,10 @@ module Backup
def prepare
FileUtils.rm_rf(backup_repos_path)
FileUtils.mkdir_p(backup_repos_path)
# Ensure the parent dir of backup_repos_path exists
FileUtils.mkdir_p(Gitlab.config.backup.path)
# Fail if somebody raced to create backup_repos_path before us
FileUtils.mkdir(backup_repos_path, mode: 0700)
end
def silent
......
......@@ -10,7 +10,11 @@ module Backup
# Copy uploads from public/uploads to backup/uploads
def dump
FileUtils.mkdir_p(backup_uploads_dir)
FileUtils.rm_rf(backup_uploads_dir)
# Ensure the parent dir of backup_uploads_dir exists
FileUtils.mkdir_p(Gitlab.config.backup.path)
# Fail if somebody raced to create backup_uploads_dir before us
FileUtils.mkdir(backup_uploads_dir, mode: 0700)
FileUtils.cp_r(app_uploads_dir, backup_dir)
end
......
......@@ -26,7 +26,12 @@ module Grack
auth!
if project && authorized_request?
@app.call(env)
if ENV['GITLAB_GRACK_AUTH_ONLY'] == '1'
# Tell gitlab-git-http-server the request is OK, and what the GL_ID is
render_grack_auth_ok
else
@app.call(env)
end
elsif @user.nil? && !@gitlab_ci
unauthorized
else
......@@ -174,6 +179,10 @@ module Grack
end
end
def render_grack_auth_ok
[200, { "Content-Type" => "application/json" }, [JSON.dump({ 'GL_ID' => Gitlab::ShellEnv.gl_id(@user) })]]
end
def render_not_found
[404, { "Content-Type" => "text/plain" }, ["Not Found"]]
end
......
......@@ -7,7 +7,7 @@ module Gitlab
def set_env(user)
# Set GL_ID env variable
if user
ENV['GL_ID'] = "user-#{user.id}"
ENV['GL_ID'] = gl_id(user)
end
end
......@@ -15,5 +15,14 @@ module Gitlab
# Reset GL_ID env variable
ENV['GL_ID'] = nil
end
def gl_id(user)
if user.present?
"user-#{user.id}"
else
# This empty string is used in the render_grack_auth_ok method
""
end
end
end
end
module Gitlab
module BitbucketImport
class Client
class Unauthorized < StandardError; end
attr_reader :consumer, :api
def initialize(access_token = nil, access_token_secret = nil)
......@@ -46,23 +48,23 @@ module Gitlab
end
def user
JSON.parse(api.get("/api/1.0/user").body)
JSON.parse(get("/api/1.0/user").body)
end
def issues(project_identifier)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/issues").body)
JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues").body)
end
def issue_comments(project_identifier, issue_id)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body)
JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body)
end
def project(project_identifier)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}").body)
JSON.parse(get("/api/1.0/repositories/#{project_identifier}").body)
end
def find_deploy_key(project_identifier, key)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/deploy-keys").body).find do |deploy_key|
JSON.parse(get("/api/1.0/repositories/#{project_identifier}/deploy-keys").body).find do |deploy_key|
deploy_key["key"].chomp == key.chomp
end
end
......@@ -82,11 +84,18 @@ module Gitlab
end
def projects
JSON.parse(api.get("/api/1.0/user/repositories").body).select { |repo| repo["scm"] == "git" }
JSON.parse(get("/api/1.0/user/repositories").body).select { |repo| repo["scm"] == "git" }
end
private
def get(url)
response = api.get(url)
raise Unauthorized if (400..499).include?(response.code.to_i)
response
end
def config
Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket"}
end
......
......@@ -98,15 +98,25 @@ module Gitlab
#
# Returns a String
def path_type(path)
if repository.tree(current_sha, path).entries.any?
unescaped_path = Addressable::URI.unescape(path)
if tree?(unescaped_path)
'tree'
elsif repository.blob_at(current_sha, path).try(:image?)
elsif image?(unescaped_path)
'raw'
else
'blob'
end
end
def tree?(path)
repository.tree(current_sha, path).entries.any?
end
def image?(path)
repository.blob_at(current_sha, path).try(:image?)
end
def current_sha
context[:commit].try(:id) ||
ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha
......
module Gitlab
module OAuth
class Provider
def self.names
providers = []
def self.providers
Devise.omniauth_providers
end
Gitlab.config.ldap.servers.values.each do |server|
providers << server['provider_name']
end
def self.enabled?(name)
providers.include?(name.to_sym)
end
Gitlab.config.omniauth.providers.each do |provider|
providers << provider['name']
def self.ldap_provider?(name)
name.to_s.start_with?('ldap')
end
def self.config_for(name)
name = name.to_s
if ldap_provider?(name)
Gitlab::LDAP::Config.new(name).options
else
Gitlab.config.omniauth.providers.find { |provider| provider.name == name }
end
end
providers
def self.label_for(name)
config = config_for(name)
(config && config['label']) || name.to_s.titleize
end
end
end
......
......@@ -22,10 +22,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
ERB::Util.html_escape_once(text)
end
# Stolen from Rugments::Plugins::Redcarpet as this module is not required
# from Rugments's gem root.
# Stolen from Rouge::Plugins::Redcarpet as this module is not required
# from Rouge's gem root.
def block_code(code, language)
lexer = Rugments::Lexer.find_fancy(language, code) || Rugments::Lexers::PlainText
lexer = Rouge::Lexer.find_fancy(language, code) || Rouge::Lexers::PlainText
# XXX HACK: Redcarpet strips hard tabs out of code blocks,
# so we assume you're not using leading spaces that aren't tabs,
......@@ -34,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
code.gsub!(/^ /, "\t")
end
formatter = Rugments::Formatters::HTML.new(
formatter = Rouge::Formatters::HTMLGitlab.new(
cssclass: "code highlight #{@color_scheme} #{lexer.tag}"
)
formatter.format(lexer.lex(code))
......
require 'cgi'
module Rouge
module Formatters
class HTMLGitlab < Rouge::Formatter
tag 'html_gitlab'
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
#
# [+nowrap+] If set to True, don't wrap the output at all, not
# even inside a <tt><pre></tt> tag (default: false).
# [+cssclass+] CSS class for the wrapping <tt><div></tt> tag
# (default: 'highlight').
# [+linenos+] If set to 'table', output line numbers as a table
# with two cells, one containing the line numbers,
# the other the whole code. This is copy paste friendly,
# but may cause alignment problems with some browsers
# or fonts. If set to 'inline', the line numbers will
# be integrated in the <tt><pre></tt> tag that contains
# the code (default: nil).
# [+linenostart+] The line number for the first line (default: 1).
# [+lineanchors+] If set to true the formatter will wrap each output
# line in an anchor tag with a name of L-linenumber.
# This allows easy linking to certain lines
# (default: false).
# [+lineanchorsid+] If lineanchors is true the name of the anchors can
# be changed with lineanchorsid to e.g. foo-linenumber
# (default: 'L').
# [+anchorlinenos+] If set to true, will wrap line numbers in <tt><a></tt>
# tags. Used in combination with linenos and lineanchors
# (default: false).
# [+inline_theme+] Inline CSS styles for the <pre> tag (default: false).
def initialize(
nowrap: false,
cssclass: 'highlight',
linenos: nil,
linenostart: 1,
lineanchors: false,
lineanchorsid: 'L',
anchorlinenos: false,
inline_theme: nil
)
@nowrap = nowrap
@cssclass = cssclass
@linenos = linenos
@linenostart = linenostart
@lineanchors = lineanchors
@lineanchorsid = lineanchorsid
@anchorlinenos = anchorlinenos
@inline_theme = Theme.find(@inline_theme).new if @inline_theme.is_a?(String)
end
def render(tokens)
case @linenos
when 'table'
render_tableized(tokens)
when 'inline'
render_untableized(tokens)
else
render_untableized(tokens)
end
end
alias_method :format, :render
private
def render_untableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
html << wrap_lines(data[:code])
html << "</code></pre>\n" unless @nowrap
html
end
def render_tableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<div class=\"#{@cssclass}\">" unless @nowrap
html << '<table><tbody>'
html << "<td class=\"linenos\"><pre>"
html << wrap_linenos(data[:numbers])
html << '</pre></td>'
html << "<td class=\"lines\"><pre><code>"
html << wrap_lines(data[:code])
html << '</code></pre></td>'
html << '</tbody></table>'
html << '</div>' unless @nowrap
html
end
def process_tokens(tokens)
num_lines = 0
last_val = ''
rendered = ''
tokens.each do |tok, val|
last_val = val
num_lines += val.scan(/\n/).size
rendered << span(tok, val)
end
numbers = (@linenostart..num_lines + @linenostart - 1).to_a
{ numbers: numbers, code: rendered }
end
def wrap_linenos(numbers)
if @anchorlinenos
numbers.map! do |number|
"<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
end
end
numbers.join("\n")
end
def wrap_lines(rendered)
if @lineanchors
lines = rendered.split("\n")
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
if @linenos == 'inline'
"<a name=\"L#{number}\"></a>" \
"<span class=\"linenos\">#{number}</span>" \
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
else
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
end
end
lines.join("\n")
else
if @linenos == 'inline'
lines = rendered.split("\n")
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
"<span class=\"linenos\">#{number}</span>#{line}"
end
lines.join("\n")
else
rendered
end
end
end
def wrap_values(val, element)
lines = val.split("\n")
lines = lines.map{ |x| "<span #{element}>#{x}</span>" }
lines.join("\n")
end
def span(tok, val)
# http://stackoverflow.com/a/1600584/2587286
val = CGI.escapeHTML(val)
if tok.shortname.empty?
val
else
# In the case of multi-line values (e.g. comments), we need to apply
# styling to each line since span elements are inline.
if @inline_theme
rules = @inline_theme.style_for(tok).rendered_rules
wrap_values(val, "style=\"#{rules.to_a.join(';')}\"")
else
wrap_values(val, "class=\"#{tok.shortname}\"")
end
end
end
end
end
end
......@@ -38,6 +38,11 @@ upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
}
## Experimental: gitlab-git-http-server
# upstream gitlab-git-http-server {
# server localhost:8181;
# }
## Normal HTTP host
server {
## Either remove "default_server" from the listen line below,
......@@ -109,6 +114,26 @@ server {
proxy_pass http://gitlab;
}
## Experimental: send Git HTTP traffic to gitlab-git-http-server instead of Unicorn
# location ~ [-\/\w\.]+\.git\/ {
# ## If you use HTTPS make sure you disable gzip compression
# ## to be safe against BREACH attack.
# # gzip off;
# ## https://github.com/gitlabhq/gitlabhq/issues/694
# ## Some requests take more than 30 seconds.
# proxy_read_timeout 300;
# proxy_connect_timeout 300;
# proxy_redirect off;
# proxy_set_header Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_pass http://gitlab-git-http-server;
# }
## Enable gzip compression as per rails guide:
## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
## WARNING: If you are using relative urls remove the block below
......
......@@ -42,6 +42,11 @@ upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
}
## Experimental: gitlab-git-http-server
# upstream gitlab-git-http-server {
# server localhost:8181;
# }
## Redirects all HTTP traffic to the HTTPS host
server {
## Either remove "default_server" from the listen line below,
......@@ -156,6 +161,26 @@ server {
proxy_pass http://gitlab;
}
## Experimental: send Git HTTP traffic to gitlab-git-http-server instead of Unicorn
# location ~ [-\/\w\.]+\.git\/ {
# ## If you use HTTPS make sure you disable gzip compression
# ## to be safe against BREACH attack.
# gzip off;
# ## https://github.com/gitlabhq/gitlabhq/issues/694
# ## Some requests take more than 30 seconds.
# proxy_read_timeout 300;
# proxy_connect_timeout 300;
# proxy_redirect off;
# proxy_set_header Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-Ssl on;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_pass http://gitlab-git-http-server;
# }
## Enable gzip compression as per rails guide:
## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
## WARNING: If you are using relative urls remove the block below
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment