Merge remote-tracking branch 'upstream/master' into artifacts-from-ref-and-build-name

* upstream/master: (107 commits)
  Fix external issue tracker "Issues" link leading to 404s
  Fix CHANGELOG entries related to 8.11 release.
  Fix changelog
  Reduce contributions calendar data payload
  Add lock_version to merge_requests table
  Add hover color to emoji icon
  Fix wrong Koding link
  Capitalize mentioned issue timeline notes
  Fix groups sort dropdown alignment
  Use icon helper
  Fix inline emoji text alignment
  Adds response mime type to transaction metric action when it's not HTML
  Moved two 8.11 changelog entries to 8.12
  Fix markdown link in doc_styleguide.md
  Display project icon from default branch
  Reduce number of database queries on builds tab
  Update CHANGELOG
  Update Issue board documentation
  Label list shows all issues (opened or closed) with that label
  Update CHANGELOG
  ...
This commit is contained in:
Lin Jen-Shin 2016-08-26 17:31:12 +08:00
commit de80cbfe3f
145 changed files with 1721 additions and 750 deletions

View File

@ -1,13 +1,56 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased)
- Add two-factor recovery endpoint to internal API !5510
- Change merge_error column from string to text type
- Reduce contributions calendar data payload (ClemMakesApps)
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
- Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
- Add hover color to emoji icon (ClemMakesApps)
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
- Add `wiki_page_events` to project hook APIs (Ben Boeckel)
- Remove Gitorious import
- Add Sentry logging to API calls
- Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
- Fix groups sort dropdown alignment (ClemMakesApps)
- Added tests for diff notes
- Add a button to download latest successful artifacts for branches and tags !5142
v 8.11.1 (unreleased)
- Add delimiter to project stars and forks count (ClemMakesApps)
- Fix badge count alignment (ClemMakesApps)
- Fix branch title trailing space on hover (ClemMakesApps)
- Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
- Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison)
- Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
- Update merge_requests.md with a simpler way to check out a merge request. !5944
- Fix button missing type (ClemMakesApps)
- Move to project dropdown with infinite scroll for better performance
- Load branches asynchronously in Cherry Pick and Revert dialogs.
- Add merge request versions !5467
- Change using size to use count and caching it for number of group members. !5935
- Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck)
- Reduce number of database queries on builds tab
- Capitalize mentioned issue timeline notes (ClemMakesApps)
- Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
- Adds response mime type to transaction metric action when it's not HTML
v 8.11.3 (unreleased)
- Allow system info page to handle case where info is unavailable
- Label list shows all issues (opened or closed) with that label
- Don't show resolve conflicts link before MR status is updated
- Don't prevent viewing the MR when git refs for conflicts can't be found on disk
- Fix external issue tracker "Issues" link leading to 404s
v 8.11.2
- Show "Create Merge Request" widget for push events to fork projects on the source project. !5978
- Use gitlab-workhorse 0.7.11 !5983
- Does not halt the GitHub import process when an error occurs. !5763
- Fix file links on project page when default view is Files !5933
- Fixed enter key in search input not working !5888
v 8.11.1
- Pulled due to packaging error.
v 8.11.0
- Use test coverage value from the latest successful pipeline in badge. !5862
@ -16,7 +59,6 @@ v 8.11.0
- Add Koding (online IDE) integration
- Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Add delimiter to project stars and forks count (ClemMakesApps)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix adding line comments on the initial commit to a repo !5900
- Fix the title of the toggle dropdown button. !5515 (herminiotorres)
@ -32,7 +74,6 @@ v 8.11.0
- Use long options for curl examples in documentation !5703 (winniehell)
- Added tooltip listing label names to the labels value in the collapsed issuable sidebar
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Fix badge count alignment (ClemMakesApps)
- GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Allow naming U2F devices !5833
@ -73,7 +114,6 @@ v 8.11.0
- Enforce 2FA restrictions on API authentication endpoints !5820
- Limit git rev-list output count to one in forced push check
- Show deployment status on merge requests with external URLs
- Fix branch title trailing space on hover (ClemMakesApps)
- Clean up unused routes (Josef Strzibny)
- Fix issue on empty project to allow developers to only push to protected branches if given permission
- API: Add enpoints for pipelines
@ -90,7 +130,6 @@ v 8.11.0
- Fix devise deprecation warnings.
- Check for 2FA when using Git over HTTP and only allow PersonalAccessTokens as password in that case !5764
- Update version_sorter and use new interface for faster tag sorting
- Load branches asynchronously in Cherry Pick and Revert dialogs.
- Optimize checking if a user has read access to a list of issues !5370
- Store all DB secrets in secrets.yml, under descriptive names !5274
- Fix syntax highlighting in file editor
@ -111,8 +150,6 @@ v 8.11.0
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Add pipeline events hook
- Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
- Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison)
- Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich)
- Make branches sortable without push permission !5462 (winniehell)
@ -124,14 +161,12 @@ v 8.11.0
- Fix search for notes which belongs to deleted objects
- Allow Akismet to be trained by submitting issues as spam or ham !5538
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
- Allow branch names ending with .json for graph and network page !5579 (winniehell)
- Add the `sprockets-es6` gem
- Improve OAuth2 client documentation (muteor)
- Fix diff comments inverted toggle bug (ClemMakesApps)
- Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- Profile requests when a header is passed
- Fix button missing type (ClemMakesApps)
- Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
- Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible
- Add commit stats in commit api. !5517 (dixpac)
@ -140,7 +175,6 @@ v 8.11.0
- edit_blob_link will use blob passed onto the options parameter
- Make error pages responsive (Takuya Noguchi)
- The performance of the project dropdown used for moving issues has been improved
- Move to project dropdown with infinite scroll for better performance
- Fix skip_repo parameter being ignored when destroying a namespace
- Add all builds into stage/job dropdowns on builds page
- Change requests_profiles resource constraint to catch virtually any file

View File

@ -1 +1 @@
0.7.10
0.7.11

View File

@ -349,5 +349,5 @@ gem 'paranoia', '~> 2.0'
gem 'health_check', '~> 2.1.0'
# System information
gem 'vmstat', '~> 2.1.1'
gem 'vmstat', '~> 2.2'
gem 'sys-filesystem', '~> 1.1.6'

View File

@ -772,7 +772,7 @@ GEM
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
vmstat (2.1.1)
vmstat (2.2.0)
warden (1.2.6)
rack (>= 1.0)
web-console (2.3.0)
@ -980,7 +980,7 @@ DEPENDENCIES
unicorn-worker-killer (~> 0.4.2)
version_sorter (~> 2.1.0)
virtus (~> 1.0.1)
vmstat (~> 2.1.1)
vmstat (~> 2.2)
web-console (~> 2.0)
webmock (~> 1.21.0)
wikicloth (= 0.8.1)

View File

@ -1,6 +1,7 @@
# GitLab
[![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
## Canonical source
@ -69,7 +70,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.1
- Ruby (MRI) 2.3
- Git 2.7.4+
- Redis 2.8+
- MySQL or PostgreSQL

View File

@ -1,4 +1,3 @@
window.gl = window.gl || {};
((global) => {
const MAX_MESSAGE_LENGTH = 500;
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
@ -36,4 +35,4 @@ window.gl = window.gl || {};
}
global.AbuseReports = AbuseReports;
})(window.gl);
})(window.gl || (window.gl = {}));

View File

@ -26,7 +26,7 @@
event_filters = $.cookie("event_filter");
filter = sender.attr("id").split("_")[0];
$.cookie("event_filter", (event_filters !== filter ? filter : ""), {
path: '/'
path: gon.relative_url_root || '/'
});
if (event_filters !== filter) {
return sender.closest('li').toggleClass("active");

View File

@ -288,7 +288,7 @@
new Aside();
if ($window.width() < 1024 && $.cookie('pin_nav') === 'true') {
$.cookie('pin_nav', 'false', {
path: '/',
path: gon.relative_url_root || '/',
expires: 365 * 10
});
$('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
@ -313,7 +313,7 @@
$topNav.removeClass('header-pinned-nav').toggleClass('header-collapsed header-expanded');
}
$.cookie('pin_nav', doPinNav, {
path: '/',
path: gon.relative_url_root || '/',
expires: 365 * 10
});
if ($.cookie('pin_nav') === 'true' || doPinNav) {

View File

@ -320,6 +320,7 @@
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
frequentlyUsedEmojis.push(emoji);
return $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), {
path: gon.relative_url_root || '/',
expires: 365
});
};

View File

@ -1,10 +1,26 @@
(function() {
(function(w) {
$(function() {
return $("body").on("click", ".js-toggle-button", function(e) {
$(this).find('i').toggleClass('fa fa-chevron-down').toggleClass('fa fa-chevron-up');
$(this).closest(".js-toggle-container").find(".js-toggle-content").toggle();
return e.preventDefault();
$('.js-toggle-button').on('click', function(e) {
e.preventDefault();
$(this)
.find('.fa')
.toggleClass('fa-chevron-down fa-chevron-up')
.end()
.closest('.js-toggle-container')
.find('.js-toggle-content')
.toggle()
;
});
});
}).call(this);
// If we're accessing a permalink, ensure it is not inside a
// closed js-toggle-container!
var hash = w.gl.utils.getLocationHash();
var anchor = hash && document.getElementById(hash);
var container = anchor && $(anchor).closest('.js-toggle-container');
if (container && container.find('.js-toggle-content').is(':hidden')) {
container.find('.js-toggle-button').trigger('click');
anchor.scrollIntoView();
}
});
})(window);

View File

@ -67,6 +67,14 @@
$.timeago.settings.strings = tmpLocale;
};
w.gl.utils.getDayDifference = function(a, b) {
var millisecondsPerDay = 1000 * 60 * 60 * 24;
var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return Math.floor((date2 - date1) / millisecondsPerDay);
}
})(window);
}).call(this);

View File

@ -43,7 +43,7 @@
}
return newUrl;
};
return w.gl.utils.removeParamQueryString = function(url, param) {
w.gl.utils.removeParamQueryString = function(url, param) {
var urlVariables, variables;
url = decodeURIComponent(url);
urlVariables = url.split('&');
@ -59,6 +59,16 @@
return results;
})()).join('&');
};
w.gl.utils.getLocationHash = function(url) {
var hashIndex;
if (typeof url === 'undefined') {
// Note: We can't use window.location.hash here because it's
// not consistent across browsers - Firefox will pre-decode it
url = window.location.href;
}
hashIndex = url.indexOf('#');
return hashIndex === -1 ? null : url.substring(hashIndex + 1);
};
})(window);
}).call(this);

View File

@ -17,19 +17,15 @@
return $(this).parents('form').submit();
});
$('.hide-no-ssh-message').on('click', function(e) {
var path;
path = '/';
$.cookie('hide_no_ssh_message', 'false', {
path: path
path: gon.relative_url_root || '/'
});
$(this).parents('.no-ssh-key-message').remove();
return e.preventDefault();
});
$('.hide-no-password-message').on('click', function(e) {
var path;
path = '/';
$.cookie('hide_no_password_message', 'false', {
path: path
path: gon.relative_url_root || '/'
});
$(this).parents('.no-password-message').remove();
return e.preventDefault();

View File

@ -30,7 +30,7 @@
}
if (!triggered) {
return $.cookie("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'), {
path: '/'
path: gon.relative_url_root || '/'
});
}
});

View File

@ -7,10 +7,8 @@
});
this.initTabs();
$('.hide-project-limit-message').on('click', function(e) {
var path;
path = '/';
$.cookie('hide_project_limit_message', 'false', {
path: path
path: gon.relative_url_root || '/'
});
$(this).parents('.project-limit-message').remove();
return e.preventDefault();

View File

@ -3,7 +3,6 @@
this.Calendar = (function() {
function Calendar(timestamps, calendar_activities_path) {
var group, i;
this.calendar_activities_path = calendar_activities_path;
this.clickDay = bind(this.clickDay, this);
this.currentSelectedDate = '';
@ -13,26 +12,36 @@
this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
this.months = [];
this.timestampsTmp = [];
i = 0;
group = 0;
_.each(timestamps, (function(_this) {
return function(count, date) {
var day, innerArray, newDate;
newDate = new Date(parseInt(date) * 1000);
day = newDate.getDay();
if ((day === 0 && i !== 0) || i === 0) {
_this.timestampsTmp.push([]);
group++;
}
innerArray = _this.timestampsTmp[group - 1];
innerArray.push({
count: count,
date: newDate,
day: day
});
return i++;
};
})(this));
var group = 0;
var today = new Date()
today.setHours(0, 0, 0, 0, 0);
var oneYearAgo = new Date(today);
oneYearAgo.setFullYear(today.getFullYear() - 1);
var days = gl.utils.getDayDifference(oneYearAgo, today);
for(var i = 0; i <= days; i++) {
var date = new Date(oneYearAgo);
date.setDate(date.getDate() + i);
var day = date.getDay();
var count = timestamps[date.getTime() * 0.001];
if ((day === 0 && i !== 0) || i === 0) {
this.timestampsTmp.push([]);
group++;
}
var innerArray = this.timestampsTmp[group - 1];
innerArray.push({
count: count || 0,
date: date,
day: day
});
}
this.colorKey = this.initColorKey();
this.color = this.initColor();
this.renderSvg(group);

View File

@ -248,7 +248,7 @@ li.note {
img.emoji {
height: 20px;
vertical-align: middle;
vertical-align: top;
width: 20px;
}

View File

@ -17,6 +17,12 @@
.dropdown {
position: relative;
.btn-link {
&:hover {
cursor: pointer;
}
}
}
.open {

View File

@ -150,7 +150,7 @@
border-top: 1px solid $border-color;
border-bottom: 1px solid $border-color;
max-height: 300px;
overflow: scroll;
overflow: auto;
svg {
position: relative;

View File

@ -1,22 +1,3 @@
i.icon-gitorious {
display: inline-block;
background-position: 0 0;
background-size: contain;
background-repeat: no-repeat;
}
i.icon-gitorious-small {
background-image: image-url('gitorious-logo-blue.png');
width: 13px;
height: 13px;
}
i.icon-gitorious-big {
background-image: image-url('gitorious-logo-black.png');
width: 18px;
height: 18px;
}
.import-jobs-from-col,
.import-jobs-to-col {
width: 40%;

View File

@ -269,7 +269,7 @@
.builds {
.table-holder {
overflow-x: scroll;
overflow-x: auto;
}
}
@ -375,6 +375,16 @@
}
}
.mr-version-switch {
background: $background-color;
padding: $gl-btn-padding;
color: $gl-placeholder-color;
a.btn-link {
color: $gl-dark-link-color;
}
}
.merge-request-details {
.title {

View File

@ -281,19 +281,13 @@ ul.notes {
font-size: 17px;
}
&.js-note-delete {
i {
&:hover {
color: $gl-text-red;
}
&:hover {
.danger-highlight {
color: $gl-text-red;
}
}
&.js-note-edit {
i {
&:hover {
color: $gl-link-color;
}
.link-highlight {
color: $gl-link-color;
}
}
}

View File

@ -254,7 +254,6 @@
width: 100%;
overflow: auto;
white-space: nowrap;
max-height: 500px;
transition: max-height 0.3s, padding 0.3s;
&.graph-collapsed {
@ -265,7 +264,6 @@
.pipeline-visualization {
position: relative;
min-width: 1220px;
ul {
padding: 0;

View File

@ -29,7 +29,8 @@ class Admin::SystemInfoController < Admin::ApplicationController
]
def show
system_info = Vmstat.snapshot
@cpus = Vmstat.cpu rescue nil
@memory = Vmstat.memory rescue nil
mounts = Sys::Filesystem.mounts
@disks = []
@ -50,10 +51,5 @@ class Admin::SystemInfoController < Admin::ApplicationController
rescue Sys::Filesystem::Error
end
end
@cpus = system_info.cpus.length
@mem_used = system_info.memory.active_bytes
@mem_total = system_info.memory.total_bytes
end
end

View File

@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
include Gitlab::GonHelper
include GitlabRoutingHelper
include PageLayoutHelper
include SentryHelper
include WorkhorseHelper
before_action :authenticate_user_from_private_token!
@ -24,7 +25,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :abilities, :can?, :current_application_settings
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@ -46,28 +47,6 @@ class ApplicationController < ActionController::Base
protected
def sentry_context
if Rails.env.production? && current_application_settings.sentry_enabled
if current_user
Raven.user_context(
id: current_user.id,
email: current_user.email,
username: current_user.username,
)
end
Raven.tags_context(program: sentry_program_context)
end
end
def sentry_program_context
if Sidekiq.server?
'sidekiq'
else
'rails'
end
end
# This filter handles both private tokens and personal access tokens
def authenticate_user_from_private_token!
token_string = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
@ -271,10 +250,6 @@ class ApplicationController < ActionController::Base
Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
end
def gitorious_import_enabled?
current_application_settings.import_sources.include?('gitorious')
end
def google_code_import_enabled?
current_application_settings.import_sources.include?('google_code')
end

View File

@ -1,47 +0,0 @@
class Import::GitoriousController < Import::BaseController
before_action :verify_gitorious_import_enabled
def new
redirect_to client.authorize_url(callback_import_gitorious_url)
end
def callback
session[:gitorious_repos] = params[:repos]
redirect_to status_import_gitorious_path
end
def status
@repos = client.repos
@already_added_projects = current_user.created_projects.where(import_type: "gitorious")
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject! { |repo| already_added_projects_names.include? repo.full_name }
end
def jobs
jobs = current_user.created_projects.where(import_type: "gitorious").to_json(only: [:id, :import_status])
render json: jobs
end
def create
@repo_id = params[:repo_id]
repo = client.repo(@repo_id)
@target_namespace = params[:new_namespace].presence || repo.namespace
@project_name = repo.name
namespace = get_or_create_namespace || (render and return)
@project = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, current_user).execute
end
private
def client
@client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos])
end
def verify_gitorious_import_enabled
render_404 unless gitorious_import_enabled?
end
end

View File

@ -4,7 +4,7 @@ class Projects::AvatarsController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:destroy]
def show
@blob = @repository.blob_at_branch('master', @project.avatar_in_git)
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git)
if @blob
headers['X-Content-Type-Options'] = 'nosniff'

View File

@ -212,7 +212,7 @@ class Projects::IssuesController < Projects::ApplicationController
if action_name == 'new'
redirect_to external.new_issue_path
else
redirect_to external.issues_url
redirect_to external.project_path
end
end

View File

@ -83,12 +83,22 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diffs
apply_diff_view_cookie!
@merge_request_diff = @merge_request.merge_request_diff
@merge_request_diff =
if params[:diff_id]
@merge_request.merge_request_diffs.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
respond_to do |format|
format.html { define_discussion_vars }
format.json do
@diffs = @merge_request.diffs(diff_options)
unless @merge_request_diff.latest?
# Disable comments if browsing older version of the diff
@diff_notes_disabled = true
end
@diffs = @merge_request_diff.diffs(diff_options)
render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") }
end

View File

@ -5,7 +5,7 @@ class ProjectsController < Projects::ApplicationController
before_action :project, except: [:new, :create]
before_action :repository, except: [:new, :create]
before_action :assign_ref_vars, only: [:show], if: :repo_exists?
before_action :assign_tree_vars, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
@ -332,11 +332,4 @@ class ProjectsController < Projects::ApplicationController
def get_id
project.repository.root_ref
end
# ExtractsPath will set @id = project.path on the show route, but it has to be the
# branch name for the tree view to work correctly.
def assign_tree_vars
@id = get_id
tree
end
end

View File

@ -116,6 +116,17 @@ module ProjectsHelper
license.nickname || license.name
end
def last_push_event
return unless current_user
project_ids = [@project.id]
if fork = current_user.fork_of(@project)
project_ids << fork.id
end
current_user.recent_push(project_ids)
end
private
def get_project_nav_tabs(project, current_user)
@ -351,16 +362,6 @@ module ProjectsHelper
namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'LICENSE')
end
def last_push_event
return unless current_user
if fork = current_user.fork_of(@project)
current_user.recent_push(fork.id)
else
current_user.recent_push(@project.id)
end
end
def readme_cache_key
sha = @project.commit.try(:sha) || 'nil'
[@project.path_with_namespace, sha, "readme"].join('-')

View File

@ -0,0 +1,27 @@
module SentryHelper
def sentry_enabled?
Rails.env.production? && current_application_settings.sentry_enabled?
end
def sentry_context
return unless sentry_enabled?
if current_user
Raven.user_context(
id: current_user.id,
email: current_user.email,
username: current_user.username,
)
end
Raven.tags_context(program: sentry_program_context)
end
def sentry_program_context
if Sidekiq.server?
'sidekiq'
else
'rails'
end
end
end

View File

@ -146,7 +146,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project],
import_sources: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,

View File

@ -352,7 +352,7 @@ module Ci
end
def artifacts?
!artifacts_expired? && artifacts_file.exists?
!artifacts_expired? && self[:artifacts_file].present?
end
def artifacts_metadata?

View File

@ -1,7 +1,7 @@
module Ci
class Pipeline < ActiveRecord::Base
extend Ci::Model
include Statuseable
include HasStatus
self.table_name = 'ci_commits'
@ -83,7 +83,7 @@ module Ci
end
def stages_with_latest_statuses
statuses.latest.order(:stage_idx).group_by(&:stage)
statuses.latest.includes(project: :namespace).order(:stage_idx).group_by(&:stage)
end
def project_id

View File

@ -1,5 +1,5 @@
class CommitStatus < ActiveRecord::Base
include Statuseable
include HasStatus
include Importable
self.table_name = 'ci_builds'
@ -25,6 +25,8 @@ class CommitStatus < ActiveRecord::Base
scope :retried, -> { where.not(id: latest) }
scope :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
scope :latest_ci_stages, -> { latest.ordered.includes(project: :namespace) }
scope :retried_ci_stages, -> { retried.ordered.includes(project: :namespace) }
state_machine :status do
event :enqueue do

View File

@ -1,4 +1,4 @@
module Statuseable
module HasStatus
extend ActiveSupport::Concern
AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped]

View File

@ -10,14 +10,16 @@ class MergeRequest < ActiveRecord::Base
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
belongs_to :merge_user, class_name: "User"
has_one :merge_request_diff, dependent: :destroy
has_many :merge_request_diffs, dependent: :destroy
has_one :merge_request_diff,
-> { order('merge_request_diffs.id DESC') }
has_many :events, as: :target, dependent: :destroy
serialize :merge_params, Hash
after_create :create_merge_request_diff, unless: :importing?
after_update :update_merge_request_diff
after_create :ensure_merge_request_diff, unless: :importing?
after_update :reload_diff_if_branch_changed
delegate :commits, :real_size, to: :merge_request_diff, prefix: nil
@ -170,10 +172,10 @@ class MergeRequest < ActiveRecord::Base
end
def diffs(diff_options = nil)
if self.compare
self.compare.diffs(diff_options)
if compare
compare.diffs(diff_options)
else
Gitlab::Diff::FileCollection::MergeRequest.new(self, diff_options: diff_options)
merge_request_diff.diffs(diff_options)
end
end
@ -184,8 +186,8 @@ class MergeRequest < ActiveRecord::Base
def diff_base_commit
if persisted?
merge_request_diff.base_commit
elsif diff_start_commit && diff_head_commit
self.target_project.merge_base_commit(diff_start_sha, diff_head_sha)
else
branch_merge_base_commit
end
end
@ -246,6 +248,15 @@ class MergeRequest < ActiveRecord::Base
target_project.repository.commit(target_branch) if target_branch_ref
end
def branch_merge_base_commit
start_sha = target_branch_sha
head_sha = source_branch_sha
if start_sha && head_sha
target_project.merge_base_commit(start_sha, head_sha)
end
end
def target_branch_sha
@target_branch_sha || target_branch_head.try(:sha)
end
@ -267,16 +278,16 @@ class MergeRequest < ActiveRecord::Base
# Return diff_refs instance trying to not touch the git repository
def diff_sha_refs
if merge_request_diff && merge_request_diff.diff_refs_by_sha?
return Gitlab::Diff::DiffRefs.new(
base_sha: merge_request_diff.base_commit_sha,
start_sha: merge_request_diff.start_commit_sha,
head_sha: merge_request_diff.head_commit_sha
)
merge_request_diff.diff_refs
else
diff_refs
end
end
def branch_merge_base_sha
branch_merge_base_commit.try(:sha)
end
def validate_branches
if target_project == source_project && target_branch == source_branch
errors.add :branch_conflict, "You can not use same project/branch for source and target"
@ -309,21 +320,31 @@ class MergeRequest < ActiveRecord::Base
end
end
def update_merge_request_diff
def ensure_merge_request_diff
merge_request_diff || create_merge_request_diff
end
def create_merge_request_diff
merge_request_diffs.create
reload_merge_request_diff
end
def reload_merge_request_diff
merge_request_diff(true)
end
def reload_diff_if_branch_changed
if source_branch_changed? || target_branch_changed?
reload_diff
end
end
def reload_diff
return unless merge_request_diff && open?
return unless open?
old_diff_refs = self.diff_refs
merge_request_diff.reload_content
create_merge_request_diff
MergeRequests::MergeRequestDiffCacheService.new.execute(self)
new_diff_refs = self.diff_refs
update_diff_notes_positions(
@ -779,8 +800,12 @@ class MergeRequest < ActiveRecord::Base
return @conflicts_can_be_resolved_in_ui = false unless has_complete_diff_refs?
begin
@conflicts_can_be_resolved_in_ui = conflicts.files.each(&:lines)
rescue Gitlab::Conflict::Parser::ParserError, Gitlab::Conflict::FileCollection::ConflictSideMissing
# Try to parse each conflict. If the MR's mergeable status hasn't been updated,
# ensure that we don't say there are conflicts to resolve when there are no conflict
# files.
conflicts.files.each(&:lines)
@conflicts_can_be_resolved_in_ui = conflicts.files.length > 0
rescue Rugged::OdbError, Gitlab::Conflict::Parser::ParserError, Gitlab::Conflict::FileCollection::ConflictSideMissing
@conflicts_can_be_resolved_in_ui = false
end
end

View File

@ -8,8 +8,6 @@ class MergeRequestDiff < ActiveRecord::Base
belongs_to :merge_request
delegate :source_branch_sha, :target_branch_sha, :target_branch, :source_branch, to: :merge_request, prefix: nil
state_machine :state, initial: :empty do
state :collected
state :overflow
@ -24,12 +22,47 @@ class MergeRequestDiff < ActiveRecord::Base
serialize :st_commits
serialize :st_diffs
after_create :reload_content, unless: :importing?
after_save :keep_around_commits, unless: :importing?
# All diff information is collected from repository after object is created.
# It allows you to override variables like head_commit_sha before getting diff.
after_create :save_git_content, unless: :importing?
def reload_content
def self.select_without_diff
select(column_names - ['st_diffs'])
end
# Collect information about commits and diff from repository
# and save it to the database as serialized data
def save_git_content
ensure_commits_sha
save_commits
reload_commits
reload_diffs
save_diffs
keep_around_commits
end
def ensure_commits_sha
merge_request.fetch_ref
self.start_commit_sha ||= merge_request.target_branch_sha
self.head_commit_sha ||= merge_request.source_branch_sha
self.base_commit_sha ||= find_base_sha
save
end
# Override head_commit_sha to keep compatibility with merge request diff
# created before version 8.4 that does not store head_commit_sha in separate db field.
def head_commit_sha
if persisted? && super.nil?
last_commit.try(:sha)
else
super
end
end
# This method will rely on repository branch sha
# in case start_commit_sha is nil. Its necesarry for old merge request diff
# created before version 8.4 to work
def safe_start_commit_sha
start_commit_sha || merge_request.target_branch_sha
end
def size
@ -38,14 +71,11 @@ class MergeRequestDiff < ActiveRecord::Base
def raw_diffs(options = {})
if options[:ignore_whitespace_change]
@raw_diffs_no_whitespace ||= begin
compare = Gitlab::Git::Compare.new(
@diffs_no_whitespace ||=
Gitlab::Git::Compare.new(
repository.raw_repository,
self.start_commit_sha || self.target_branch_sha,
self.head_commit_sha || self.source_branch_sha,
)
compare.diffs(options)
end
safe_start_commit_sha,
head_commit_sha).diffs(options)
else
@raw_diffs ||= {}
@raw_diffs[options] ||= load_diffs(st_diffs, options)
@ -56,6 +86,11 @@ class MergeRequestDiff < ActiveRecord::Base
@commits ||= load_commits(st_commits || [])
end
def reload_commits
@commits = nil
commits
end
def last_commit
commits.first
end
@ -65,55 +100,60 @@ class MergeRequestDiff < ActiveRecord::Base
end
def base_commit
return unless self.base_commit_sha
return unless base_commit_sha
project.commit(self.base_commit_sha)
project.commit(base_commit_sha)
end
def start_commit
return unless self.start_commit_sha
return unless start_commit_sha
project.commit(self.start_commit_sha)
project.commit(start_commit_sha)
end
def head_commit
return last_commit unless self.head_commit_sha
return unless head_commit_sha
project.commit(self.head_commit_sha)
project.commit(head_commit_sha)
end
def diff_refs
return unless start_commit_sha || base_commit_sha
Gitlab::Diff::DiffRefs.new(
base_sha: base_commit_sha,
start_sha: start_commit_sha,
head_sha: head_commit_sha
)
end
def diff_refs_by_sha?
base_commit_sha? && head_commit_sha? && start_commit_sha?
end
def diffs(diff_options = nil)
Gitlab::Diff::FileCollection::MergeRequestDiff.new(self, diff_options: diff_options)
end
def project
merge_request.target_project
end
def compare
@compare ||=
begin
# Update ref for merge request
merge_request.fetch_ref
Gitlab::Git::Compare.new(
repository.raw_repository,
safe_start_commit_sha,
head_commit_sha
)
end
Gitlab::Git::Compare.new(
repository.raw_repository,
self.target_branch_sha,
self.source_branch_sha
)
end
def latest?
self == merge_request.merge_request_diff
end
private
# Collect array of Git::Commit objects
# between target and source branches
def unmerged_commits
commits = compare.commits
if commits.present?
commits = Commit.decorate(commits, merge_request.source_project).reverse
end
commits
end
def dump_commits(commits)
commits.map(&:to_hash)
end
@ -122,26 +162,21 @@ class MergeRequestDiff < ActiveRecord::Base
array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) }
end
# Reload all commits related to current merge request from repo
# Load all commits related to current merge request diff from repo
# and save it as array of hashes in st_commits db field
def reload_commits
def save_commits
new_attributes = {}
commit_objects = unmerged_commits
commits = compare.commits
if commit_objects.present?
new_attributes[:st_commits] = dump_commits(commit_objects)
if commits.present?
commits = Commit.decorate(commits, merge_request.source_project).reverse
new_attributes[:st_commits] = dump_commits(commits)
end
update_columns_serialized(new_attributes)
end
# Collect array of Git::Diff objects
# between target and source branches
def unmerged_diffs
compare.diffs(Commit.max_diff_options)
end
def dump_diffs(diffs)
if diffs.respond_to?(:map)
diffs.map(&:to_hash)
@ -162,16 +197,16 @@ class MergeRequestDiff < ActiveRecord::Base
end
end
# Reload diffs between branches related to current merge request from repo
# Load diffs between branches related to current merge request diff from repo
# and save it as array of hashes in st_diffs db field
def reload_diffs
def save_diffs
new_attributes = {}
new_diffs = []
if commits.size.zero?
new_attributes[:state] = :empty
else
diff_collection = unmerged_diffs
diff_collection = compare.diffs(Commit.max_diff_options)
if diff_collection.overflow?
# Set our state to 'overflow' to make the #empty? and #collected?
@ -188,32 +223,17 @@ class MergeRequestDiff < ActiveRecord::Base
end
new_attributes[:st_diffs] = new_diffs
new_attributes[:start_commit_sha] = self.target_branch_sha
new_attributes[:head_commit_sha] = self.source_branch_sha
new_attributes[:base_commit_sha] = branch_base_sha
update_columns_serialized(new_attributes)
keep_around_commits
end
def project
merge_request.target_project
end
def repository
project.repository
end
def branch_base_commit
return unless self.source_branch_sha && self.target_branch_sha
def find_base_sha
return unless head_commit_sha && start_commit_sha
project.merge_base_commit(self.source_branch_sha, self.target_branch_sha)
end
def branch_base_sha
branch_base_commit.try(:sha)
project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha)
end
def utf8_st_diffs
@ -248,8 +268,8 @@ class MergeRequestDiff < ActiveRecord::Base
end
def keep_around_commits
repository.keep_around(target_branch_sha)
repository.keep_around(source_branch_sha)
repository.keep_around(branch_base_sha)
repository.keep_around(start_commit_sha)
repository.keep_around(head_commit_sha)
repository.keep_around(base_commit_sha)
end
end

View File

@ -471,8 +471,6 @@ class Project < ActiveRecord::Base
end
def reset_cache_and_import_attrs
update(import_error: nil)
ProjectCacheWorker.perform_async(self.id)
self.import_data.destroy if self.import_data
@ -1037,6 +1035,7 @@ class Project < ActiveRecord::Base
"refs/heads/#{branch}",
force: true)
repository.copy_gitattributes(branch)
repository.expire_avatar_cache(branch)
reload_default_branch
end

View File

@ -1065,7 +1065,7 @@ class Repository
@avatar ||= cache.fetch(:avatar) do
AVATAR_FILES.find do |file|
blob_at_branch('master', file)
blob_at_branch(root_ref, file)
end
end
end

View File

@ -489,10 +489,10 @@ class User < ActiveRecord::Base
(personal_projects.count.to_f / projects_limit) * 100
end
def recent_push(project_id = nil)
def recent_push(project_ids = nil)
# Get push events not earlier than 2 hours ago
events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
events = events.where(project_id: project_id) if project_id
events = events.where(project_id: project_ids) if project_ids
# Use the latest event that has not been pushed or merged recently
events.recent.find do |event|

View File

@ -36,7 +36,12 @@ module Boards
end
def set_state
params[:state] = list.done? ? 'closed' : 'opened'
params[:state] =
case list.list_type.to_sym
when :backlog then 'opened'
when :done then 'closed'
else 'all'
end
end
def board_label_ids

View File

@ -34,7 +34,7 @@ module Ci
end
def process_build(build, current_status)
return false unless Statuseable::COMPLETED_STATUSES.include?(current_status)
return false unless HasStatus::COMPLETED_STATUSES.include?(current_status)
if valid_statuses_for_when(build.when).include?(current_status)
build.enqueue

View File

@ -269,11 +269,11 @@ module SystemNoteService
#
# Example Note text:
#
# "mentioned in #1"
# "Mentioned in #1"
#
# "mentioned in !2"
# "Mentioned in !2"
#
# "mentioned in 54f7727c"
# "Mentioned in 54f7727c"
#
# See cross_reference_note_content.
#
@ -308,7 +308,7 @@ module SystemNoteService
# Check if a cross-reference is disallowed
#
# This method prevents adding a "mentioned in !1" note on every single commit
# This method prevents adding a "Mentioned in !1" note on every single commit
# in a merge request. Additionally, it prevents the creation of references to
# external issues (which would fail).
#
@ -417,7 +417,7 @@ module SystemNoteService
end
def cross_reference_note_prefix
'mentioned in '
'Mentioned in '
end
def cross_reference_note_content(gfm_reference)

View File

@ -148,7 +148,8 @@ class TodoService
def mark_todos_as_done_by_ids(ids, current_user)
todos = current_user.todos.where(id: ids)
marked_todos = todos.update_all(state: :done)
# Only return those that are not really on that state
marked_todos = todos.where.not(state: :done).update_all(state: :done)
current_user.update_todos_count_cache
marked_todos
end

View File

@ -9,12 +9,20 @@
.light-well
%h4 CPU
.data
%h1= "#{@cpus} cores"
- if @cpus
%h1= "#{@cpus.length} cores"
- else
= icon('warning', class: 'text-warning')
Unable to collect CPU info
.col-sm-4
.light-well
%h4 Memory
.data
%h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}"
- if @memory
%h1= "#{number_to_human_size(@memory.active_bytes)} / #{number_to_human_size(@memory.total_bytes)}"
- else
= icon('warning', class: 'text-warning')
Unable to collect memory info
.col-sm-4
.light-well
%h4 Disks

View File

@ -24,7 +24,7 @@
- else
= sort_title_recently_created
%b.caret
%ul.dropdown-menu
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to explore_groups_path(sort: sort_value_recently_created) do
= sort_title_recently_created

View File

@ -17,7 +17,7 @@
.panel-heading
%strong #{@group.name}
group members
%span.badge= @members.size
%span.badge= @members.total_count
.controls
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group

View File

@ -1,54 +0,0 @@
- page_title "Gitorious import"
- header_title "Projects", root_path
%h3.page-title
%i.icon-gitorious.icon-gitorious-big
Import projects from Gitorious.org
%p.light
Select projects you want to import.
%hr
%p
= button_tag class: "btn btn-import btn-success js-import-all" do
Import all projects
= icon("spinner spin", class: "loading-icon")
.table-responsive
%table.table.import-jobs
%colgroup.import-jobs-from-col
%colgroup.import-jobs-to-col
%colgroup.import-jobs-status-col
%thead
%tr
%th From Gitorious.org
%th To GitLab
%th Status
%tbody
- @already_added_projects.each do |project|
%tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
%td
= link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank"
%td
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
%i.fa.fa-check
done
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
started
- else
= project.human_import_status_name
- @repos.each do |repo|
%tr{id: "repo_#{repo.id}"}
%td
= link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank"
%td.import-target
= repo.full_name
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
Import
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitorious_path}", import_path: "#{import_gitorious_path}" } }

View File

@ -8,8 +8,8 @@
- if stage
&nbsp;
= stage.titleize
= render statuses.latest.ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, allow_retry: true
= render statuses.retried.ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, retried: true
= render statuses.latest_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, allow_retry: true
= render statuses.retried_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, retried: true
%tr
%td{colspan: 10}
&nbsp;

View File

@ -1,4 +1,5 @@
- if @merge_request_diff.collected?
= render 'projects/merge_requests/show/versions'
= render "projects/diffs/diffs", diffs: @diffs
- elsif @merge_request_diff.empty?
.nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}

View File

@ -0,0 +1,31 @@
- merge_request_diffs = @merge_request.merge_request_diffs.select_without_diff
- if merge_request_diffs.size > 1
.mr-version-switch
Version:
%span.dropdown.inline
%a.btn-link.dropdown-toggle{ data: {toggle: :dropdown} }
%strong.monospace<
- if @merge_request_diff.latest?
#{"latest"}
- else
#{@merge_request_diff.head_commit.short_id}
%span.caret
%ul.dropdown-menu.dropdown-menu-selectable
- merge_request_diffs.each do |merge_request_diff|
%li
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, diff_id: merge_request_diff.id), class: ('is-active' if merge_request_diff == @merge_request_diff) do
%strong.monospace
#{merge_request_diff.head_commit.short_id}
%br
%small
#{number_with_delimiter(merge_request_diff.commits.count)} #{'commit'.pluralize(merge_request_diff.commits.count)},
= time_ago_with_tooltip(merge_request_diff.created_at)
- unless @merge_request_diff.latest?
%span.prepend-left-default
= icon('info-circle')
This version is not the latest one. Comments are disabled
.pull-right
%span.monospace
#{@merge_request_diff.base_commit.short_id}..#{@merge_request_diff.head_commit.short_id}

View File

@ -59,11 +59,6 @@
= icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured?
= render 'gitlab_import_modal'
%div
- if gitorious_import_enabled?
= link_to new_import_gitorious_path, class: 'btn import_gitorious' do
%i.icon-gitorious.icon-gitorious-small
Gitorious.org
%div
- if google_code_import_enabled?
= link_to new_import_google_code_path, class: 'btn import_google_code' do

View File

@ -52,11 +52,11 @@
- if note.emoji_awardable?
= link_to '#', title: 'Award Emoji', class: 'note-action-button note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do
= icon('spinner spin')
= icon('smile-o')
= icon('smile-o', class: 'link-highlight')
- if note_editable
= link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
= icon('pencil')
= icon('pencil', class: 'link-highlight')
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do
= icon('trash-o')
.note-body{class: note_editable ? 'js-task-list-container' : ''}

View File

@ -14,6 +14,8 @@ class RepositoryImportWorker
import_url: @project.import_url,
path: @project.path_with_namespace)
project.update_column(:import_error, nil)
result = Projects::ImportService.new(project, current_user).execute
if result[:status] == :error

View File

@ -212,7 +212,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project]
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project]
Settings.gitlab['trusted_proxies'] ||= []
#

View File

@ -157,12 +157,6 @@ Rails.application.routes.draw do
get :jobs
end
resource :gitorious, only: [:create, :new], controller: :gitorious do
get :status
get :callback
get :jobs
end
resource :google_code, only: [:create, :new], controller: :google_code do
get :status
post :callback

View File

@ -0,0 +1,21 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class MergeRequestDiffRemoveUniq < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
if index_exists?(:merge_request_diffs, :merge_request_id)
remove_index :merge_request_diffs, :merge_request_id
end
end
def down
unless index_exists?(:merge_request_diffs, :merge_request_id)
add_concurrent_index :merge_request_diffs, :merge_request_id, unique: true
end
end
end

View File

@ -0,0 +1,17 @@
class MergeRequestDiffAddIndex < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
add_concurrent_index :merge_request_diffs, :merge_request_id
end
def down
if index_exists?(:merge_request_diffs, :merge_request_id)
remove_index :merge_request_diffs, :merge_request_id
end
end
end

View File

@ -593,7 +593,7 @@ ActiveRecord::Schema.define(version: 20160823081327) do
t.string "start_commit_sha"
end
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", using: :btree
create_table "merge_requests", force: :cascade do |t|
t.string "target_branch", null: false

View File

@ -2,6 +2,7 @@
## User documentation
- [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc.
- [API](api/README.md) Automate GitLab via a simple and powerful API.
- [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
@ -18,7 +19,6 @@
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [Koding](user/project/koding.md) Learn how to use Koding, the online IDE.
## Administrator documentation

View File

@ -79,7 +79,8 @@ Example response:
"labels" : [],
"subscribed" : false,
"user_notes_count": 1,
"due_date": "2016-07-22"
"due_date": "2016-07-22",
"web_url": "http://example.com/example/example/issues/6"
}
]
```
@ -156,7 +157,8 @@ Example response:
"created_at" : "2016-01-04T15:31:46.176Z",
"subscribed" : false,
"user_notes_count": 1,
"due_date": null
"due_date": null,
"web_url": "http://example.com/example/example/issues/1"
}
]
```
@ -235,7 +237,8 @@ Example response:
"created_at" : "2016-01-04T15:31:46.176Z",
"subscribed" : false,
"user_notes_count": 1,
"due_date": "2016-07-22"
"due_date": "2016-07-22",
"web_url": "http://example.com/example/example/issues/1"
}
]
```
@ -299,7 +302,8 @@ Example response:
"created_at" : "2016-01-04T15:31:46.176Z",
"subscribed": false,
"user_notes_count": 1,
"due_date": null
"due_date": null,
"web_url": "http://example.com/example/example/issues/1"
}
```
@ -323,8 +327,8 @@ POST /projects/:id/issues
| `assignee_id` | integer | no | The ID of a user to assign issue |
| `milestone_id` | integer | no | The ID of a milestone to assign issue |
| `labels` | string | no | Comma-separated label names for an issue |
| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues?title=Issues%20with%20auth&labels=bug
@ -357,7 +361,8 @@ Example response:
"milestone" : null,
"subscribed" : true,
"user_notes_count": 0,
"due_date": null
"due_date": null,
"web_url": "http://example.com/example/example/issues/14"
}
```
@ -384,8 +389,8 @@ PUT /projects/:id/issues/:issue_id
| `milestone_id` | integer | no | The ID of a milestone to assign the issue to |
| `labels` | string | no | Comma-separated label names for an issue |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85?state_event=close
@ -418,7 +423,8 @@ Example response:
"milestone" : null,
"subscribed" : true,
"user_notes_count": 0,
"due_date": "2016-07-22"
"due_date": "2016-07-22",
"web_url": "http://example.com/example/example/issues/15"
}
```
@ -496,7 +502,8 @@ Example response:
"avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon",
"web_url": "https://gitlab.example.com/u/solon.cremin"
},
"due_date": null
"due_date": null,
"web_url": "http://example.com/example/example/issues/11"
}
```
@ -551,7 +558,8 @@ Example response:
"avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon",
"web_url": "https://gitlab.example.com/u/solon.cremin"
},
"due_date": null
"due_date": null,
"web_url": "http://example.com/example/example/issues/11"
}
```
@ -607,7 +615,8 @@ Example response:
"web_url": "https://gitlab.example.com/u/orville"
},
"subscribed": false,
"due_date": null
"due_date": null,
"web_url": "http://example.com/example/example/issues/12"
}
```
@ -693,7 +702,9 @@ Example response:
"subscribed": true,
"user_notes_count": 7,
"upvotes": 0,
"downvotes": 0
"downvotes": 0,
"due_date": null,
"web_url": "http://example.com/example/example/issues/110"
},
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/issues/10",
"body": "Vel voluptas atque dicta mollitia adipisci qui at.",

View File

@ -70,7 +70,8 @@ Parameters:
"subscribed" : false,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
]
```
@ -136,7 +137,8 @@ Parameters:
"subscribed" : true,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
```
@ -239,6 +241,7 @@ Parameters:
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1",
"changes": [
{
"old_path": "VERSION",
@ -321,7 +324,8 @@ Parameters:
"subscribed" : true,
"user_notes_count": 0,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
```
@ -395,7 +399,8 @@ Parameters:
"subscribed" : true,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
```
@ -496,7 +501,8 @@ Parameters:
"subscribed" : true,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
```
@ -565,7 +571,8 @@ Parameters:
"subscribed" : true,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
```
@ -886,7 +893,8 @@ Example response:
"subscribed": true,
"user_notes_count": 7,
"should_remove_source_branch": true,
"force_remove_source_branch": false
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
},
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
"body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
@ -894,3 +902,112 @@ Example response:
"created_at": "2016-07-01T11:14:15.530Z"
}
```
## Get MR diff versions
Get a list of merge request diff versions.
```
GET /projects/:id/merge_requests/:merge_request_id/versions
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | String | yes | The ID of the project |
| `merge_request_id` | integer | yes | The ID of the merge request |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/merge_requests/1/versions
```
Example response:
```json
[{
"id": 110,
"head_commit_sha": "33e2ee8579fda5bc36accc9c6fbd0b4fefda9e30",
"base_commit_sha": "eeb57dffe83deb686a60a71c16c32f71046868fd",
"start_commit_sha": "eeb57dffe83deb686a60a71c16c32f71046868fd",
"created_at": "2016-07-26T14:44:48.926Z",
"merge_request_id": 105,
"state": "collected",
"real_size": "1"
}, {
"id": 108,
"head_commit_sha": "3eed087b29835c48015768f839d76e5ea8f07a24",
"base_commit_sha": "eeb57dffe83deb686a60a71c16c32f71046868fd",
"start_commit_sha": "eeb57dffe83deb686a60a71c16c32f71046868fd",
"created_at": "2016-07-25T14:21:33.028Z",
"merge_request_id": 105,
"state": "collected",
"real_size": "1"
}]
```
## Get a single MR diff version
Get a single merge request diff version.
```
GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | String | yes | The ID of the project |
| `merge_request_id` | integer | yes | The ID of the merge request |
| `version_id` | integer | yes | The ID of the merge request diff version |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/merge_requests/1/versions/1
```
Example response:
```json
{
"id": 110,
"head_commit_sha": "33e2ee8579fda5bc36accc9c6fbd0b4fefda9e30",
"base_commit_sha": "eeb57dffe83deb686a60a71c16c32f71046868fd",
"start_commit_sha": "eeb57dffe83deb686a60a71c16c32f71046868fd",
"created_at": "2016-07-26T14:44:48.926Z",
"merge_request_id": 105,
"state": "collected",
"real_size": "1",
"commits": [{
"id": "33e2ee8579fda5bc36accc9c6fbd0b4fefda9e30",
"short_id": "33e2ee85",
"title": "Change year to 2018",
"author_name": "Administrator",
"author_email": "admin@example.com",
"created_at": "2016-07-26T17:44:29.000+03:00",
"message": "Change year to 2018"
}, {
"id": "aa24655de48b36335556ac8a3cd8bb521f977cbd",
"short_id": "aa24655d",
"title": "Update LICENSE",
"author_name": "Administrator",
"author_email": "admin@example.com",
"created_at": "2016-07-25T17:21:53.000+03:00",
"message": "Update LICENSE"
}, {
"id": "3eed087b29835c48015768f839d76e5ea8f07a24",
"short_id": "3eed087b",
"title": "Add license",
"author_name": "Administrator",
"author_email": "admin@example.com",
"created_at": "2016-07-25T17:21:20.000+03:00",
"message": "Add license"
}],
"diffs": [{
"old_path": "LICENSE",
"new_path": "LICENSE",
"a_mode": "0",
"b_mode": "100644",
"diff": "--- /dev/null\n+++ b/LICENSE\n@@ -0,0 +1,21 @@\n+The MIT License (MIT)\n+\n+Copyright (c) 2018 Administrator\n+\n+Permission is hereby granted, free of charge, to any person obtaining a copy\n+of this software and associated documentation files (the \"Software\"), to deal\n+in the Software without restriction, including without limitation the rights\n+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n+copies of the Software, and to permit persons to whom the Software is\n+furnished to do so, subject to the following conditions:\n+\n+The above copyright notice and this permission notice shall be included in all\n+copies or substantial portions of the Software.\n+\n+THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+SOFTWARE.\n",
"new_file": true,
"renamed_file": false,
"deleted_file": false
}]
}
```

View File

@ -53,7 +53,8 @@ Parameters:
},
"expires_at": null,
"updated_at": "2012-06-28T10:52:04Z",
"created_at": "2012-06-28T10:52:04Z"
"created_at": "2012-06-28T10:52:04Z",
"web_url": "http://example.com/example/example/snippets/1"
}
```

View File

@ -84,7 +84,8 @@ Parameters:
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"shared_with_groups": []
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false
},
{
"id": 6,
@ -144,7 +145,8 @@ Parameters:
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"shared_with_groups": []
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false
}
]
```
@ -280,7 +282,8 @@ Parameters:
"group_name": "Gitlab Org",
"group_access_level": 10
}
]
],
"only_allow_merge_if_build_succeeds": false
}
```
@ -448,6 +451,7 @@ Parameters:
- `visibility_level` (optional)
- `import_url` (optional)
- `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional)
### Create project for user
@ -473,6 +477,7 @@ Parameters:
- `visibility_level` (optional)
- `import_url` (optional)
- `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional)
### Edit project
@ -499,6 +504,7 @@ Parameters:
- `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional)
- `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional)
On success, method returns 200 with the updated project. If parameters are
invalid, 400 is returned.
@ -577,7 +583,8 @@ Example response:
"forks_count": 0,
"star_count": 1,
"public_builds": true,
"shared_with_groups": []
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false
}
```
@ -643,7 +650,8 @@ Example response:
"forks_count": 0,
"star_count": 0,
"public_builds": true,
"shared_with_groups": []
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false
}
```
@ -729,7 +737,8 @@ Example response:
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": []
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false
}
```
@ -815,7 +824,8 @@ Example response:
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": []
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false
}
```
@ -914,7 +924,11 @@ Parameters:
"push_events": true,
"issues_events": true,
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"build_events": true,
"pipeline_events": true,
"wiki_page_events": true,
"enable_ssl_verification": true,
"created_at": "2012-10-12T17:04:47Z"
}
@ -937,6 +951,9 @@ Parameters:
- `merge_requests_events` - Trigger hook on merge_requests events
- `tag_push_events` - Trigger hook on push_tag events
- `note_events` - Trigger hook on note events
- `build_events` - Trigger hook on build events
- `pipeline_events` - Trigger hook on pipeline events
- `wiki_page_events` - Trigger hook on wiki page events
- `enable_ssl_verification` - Do SSL verification when triggering the hook
### Edit project hook
@ -957,6 +974,9 @@ Parameters:
- `merge_requests_events` - Trigger hook on merge_requests events
- `tag_push_events` - Trigger hook on push_tag events
- `note_events` - Trigger hook on note events
- `build_events` - Trigger hook on build events
- `pipeline_events` - Trigger hook on pipeline events
- `wiki_page_events` - Trigger hook on wiki page events
- `enable_ssl_verification` - Do SSL verification when triggering the hook
### Delete project hook

View File

@ -6,7 +6,7 @@ it organized and easy to find.
## Location and naming of documents
>**Note:**
These guidelines derive from the discussion taken place in issue [#3349](ce-3349).
These guidelines derive from the discussion taken place in issue [#3349][ce-3349].
The documentation hierarchy can be vastly improved by providing a better layout
and organization of directories.

View File

@ -397,7 +397,7 @@ If you are not using Linux you may have to run `gmake` instead of
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
sudo -u git -H git checkout v0.7.10
sudo -u git -H git checkout v0.7.11
sudo -u git -H make
### Initialize Database and Activate Advanced Features

View File

@ -32,7 +32,7 @@ Please consider using a virtual machine to run GitLab.
## Ruby versions
GitLab requires Ruby (MRI) 2.1.x and currently does not work with versions 2.2 or 2.3.
GitLab requires Ruby (MRI) 2.3. Support for Ruby versions below 2.3 (2.1, 2.2) will stop with GitLab 8.13.
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab

View File

@ -15,7 +15,7 @@ See the documentation below for details on how to configure these services.
- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages
- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users
- [Akismet](akismet.md) Configure Akismet to stop spam
- [Koding](koding.md) Configure Koding to use IDE integration
- [Koding](../administration/integration/koding.md) Configure Koding to use IDE integration
GitLab Enterprise Edition contains [advanced Jenkins support][jenkins].

View File

@ -0,0 +1,3 @@
# Account Security
- [Two-Factor Authentication](two_factor_authentication.md)

View File

@ -0,0 +1,68 @@
# Two-Factor Authentication
## Recovery options
If you lose your code generation device (such as your mobile phone) and you need
to disable two-factor authentication on your account, you have several options.
### Use a saved recovery code
When you enabled two-factor authentication for your account, a series of
recovery codes were generated. If you saved those codes somewhere safe, you
may use one to sign in.
First, enter your username/email and password on the GitLab sign in page. When
prompted for a two-factor code, enter one of the recovery codes you saved
previously.
> **Note:** Once a particular recovery code has been used, it cannot be used again.
You may still use the other saved recovery codes at a later time.
### Generate new recovery codes using SSH
It's not uncommon for users to forget to save the recovery codes when enabling
two-factor authentication. If you have an SSH key added to your GitLab account,
you can generate a new set of recovery codes using SSH.
Run `ssh git@gitlab.example.com 2fa_recovery_codes`. You will be prompted to
confirm that you wish to generate new codes. If you choose to continue, any
previously saved codes will be invalidated.
```bash
$ ssh git@gitlab.example.com 2fa_recovery_codes
Are you sure you want to generate new two-factor recovery codes?
Any existing recovery codes you saved will be invalidated. (yes/no)
yes
Your two-factor authentication recovery codes are:
119135e5a3ebce8e
11f6v2a498810dcd
3924c7ab2089c902
e79a3398bfe4f224
34bd7b74adbc8861
f061691d5107df1a
169bf32a18e63e7f
b510e7422e81c947
20dbed24c5e74663
df9d3b9403b9c9f0
During sign in, use one of the codes above when prompted for
your two-factor code. Then, visit your Profile Settings and add
a new device so you do not lose access to your account again.
```
Next, go to the GitLab sign in page and enter your username/email and password.
When prompted for a two-factor code, enter one of the recovery codes obtained
from the command line output.
> **Note:** After signing in, you should immediately visit your **Profile Settings
-> Account** to set up two-factor authentication with a new device.
### Ask a GitLab administrator to disable two-factor on your account
If the above two methods are not possible, you may ask a GitLab global
administrator to disable two-factor authentication for your account. Please
be aware that this will temporarily leave your account in a less secure state.
You should sign in and re-enable two-factor authentication as soon as possible
after the administrator disables it.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 269 KiB

View File

@ -31,10 +31,9 @@ Below is a table of the definitions used for GitLab's Issue Board.
There are three types of lists, the ones you create based on your labels, and
two default:
- **Backlog** (default): shows all issues that do not fall in one of the other
lists. Always appears on the very left.
- **Done** (default): shows all closed issues. Always appears on the very right.
- Label list: a list based on a label. It shows all issues with that label.
- **Backlog** (default): shows all opened issues that do not fall in one of the other lists. Always appears on the very left.
- **Done** (default): shows all closed issues that do not fall in one of the other lists. Always appears on the very right.
- Label list: a list based on a label. It shows all opened or closed issues with that label.
![GitLab Issue Board](img/issue_board.png)

View File

@ -26,6 +26,7 @@ this is similar to performing `git checkout feature; git merge master` locally.
GitLab allows resolving conflicts in a file where all of the below are true:
- The file is text, not binary
- The file is in a UTF-8 compatible encoding
- The file does not already contain conflict markers
- The file, with conflict markers added, is not over 200 KB in size
- The file exists under the same path in both branches

View File

@ -115,7 +115,7 @@ In this flow it is not common to have a production branch (or git flow master br
Merge or pull requests are created in a git management application and ask an assigned person to merge two branches.
Tools such as GitHub and Bitbucket choose the name pull request since the first manual action would be to pull the feature branch.
Tools such as GitLab and Gitorious choose the name merge request since that is the final action that is requested of the assignee.
Tools such as GitLab and others choose the name merge request since that is the final action that is requested of the assignee.
In this article we'll refer to them as merge requests.
If you work on a feature branch for more than a few hours it is good to share the intermediate result with the rest of the team.

View File

@ -15,6 +15,25 @@ Please note that you need to have builds configured to enable this feature.
## Checkout merge requests locally
### By adding a git alias
Add the following alias to your `~/.gitconfig`:
```
[alias]
mr = !sh -c 'git fetch $1 merge-requests/$2/head:mr-$1-$2 && git checkout mr-$1-$2' -
```
Now you can check out a particular merge request from any repository and any remote, e.g. to check out a merge request number 5 as shown in GitLab from the `upstream` remote, do:
```
$ git mr upstream 5
```
This will fetch the merge request into a local `mr-upstream-5` branch and check it out.
### By modifying `.git/config` for a given repository
Locate the section for your GitLab remote in the `.git/config` file. It looks like this:
```
@ -34,7 +53,7 @@ It should look like this:
fetch = +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*
```
Now you can fetch all the merge requests requests:
Now you can fetch all the merge requests:
```
$ git fetch origin
@ -61,3 +80,11 @@ If you click the "Hide whitespace changes" button, you can see the diff without
It is also working on commits compare view.
![Commit Compare](merge_requests/commit_compare.png)
## Merge Requests versions
Every time you push to merge request branch, a new version of merge request diff
is created. When you visit the merge request page you see latest version of changes.
However you can select an older one from version dropdown
![Merge Request Versions](merge_requests/versions.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -24,7 +24,7 @@ Feature: Project Merge Requests
Scenario: I should see target branch when it is different from default
Given project "Shop" have "Bug NS-06" open merge request
When I visit project "Shop" merge requests page
Then I should see "other_branch" branch
Then I should see "feature_conflict" branch
Scenario: I should not see the numbers of diverged commits if the branch is rebased on the target
Given project "Shop" have "Bug NS-07" open merge request with rebased branch

View File

@ -18,7 +18,6 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
expect(page).to have_link('GitHub')
expect(page).to have_link('Bitbucket')
expect(page).to have_link('GitLab.com')
expect(page).to have_link('Gitorious.org')
expect(page).to have_link('Google Code')
expect(page).to have_link('Repo by URL')
end

View File

@ -58,8 +58,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
expect(find('.merge-request-info')).not_to have_content "master"
end
step 'I should see "other_branch" branch' do
expect(page).to have_content "other_branch"
step 'I should see "feature_conflict" branch' do
expect(page).to have_content "feature_conflict"
end
step 'I should see "Bug NS-04" in merge requests' do
@ -124,7 +124,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
source_project: project,
target_project: project,
source_branch: 'fix',
target_branch: 'other_branch',
target_branch: 'feature_conflict',
author: project.users.first,
description: "# Description header"
)

View File

@ -179,7 +179,7 @@ module SharedIssuable
project = Project.find_by(name: from_project_name)
expect(page).to have_content(user_name)
expect(page).to have_content("mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}")
expect(page).to have_content("Mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}")
end
def expect_sidebar_content(content)

View File

@ -18,22 +18,14 @@ module API
end
rescue_from :all do |exception|
# lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60
# why is this not wrapped in something reusable?
trace = exception.backtrace
message = "\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
API.logger.add Logger::FATAL, message
rack_response({ 'message' => '500 Internal Server Error' }.to_json, 500)
handle_api_exception(exception)
end
format :json
content_type :txt, "text/plain"
# Ensure the namespace is right, otherwise we might load Grape::API::Helpers
helpers ::SentryHelper
helpers ::API::Helpers
mount ::API::AccessRequests
@ -75,5 +67,6 @@ module API
mount ::API::Triggers
mount ::API::Users
mount ::API::Variables
mount ::API::MergeRequestDiffs
end
end

View File

@ -49,7 +49,7 @@ module API
class ProjectHook < Hook
expose :project_id, :push_events
expose :issues_events, :merge_requests_events, :tag_push_events
expose :note_events, :build_events, :pipeline_events
expose :note_events, :build_events, :pipeline_events, :wiki_page_events
expose :enable_ssl_verification
end
@ -90,6 +90,7 @@ module API
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options)
end
expose :only_allow_merge_if_build_succeeds
end
class Member < UserBasic
@ -177,6 +178,10 @@ module API
# TODO (rspeicher): Deprecated; remove in 9.0
expose(:expires_at) { |snippet| nil }
expose :web_url do |snippet, options|
Gitlab::UrlBuilder.build(snippet)
end
end
class ProjectEntity < Grape::Entity
@ -206,6 +211,10 @@ module API
expose :user_notes_count
expose :upvotes, :downvotes
expose :due_date
expose :web_url do |issue, options|
Gitlab::UrlBuilder.build(issue)
end
end
class ExternalIssue < Grape::Entity
@ -229,6 +238,10 @@ module API
expose :user_notes_count
expose :should_remove_source_branch?, as: :should_remove_source_branch
expose :force_remove_source_branch?, as: :force_remove_source_branch
expose :web_url do |merge_request, options|
Gitlab::UrlBuilder.build(merge_request)
end
end
class MergeRequestChanges < MergeRequest
@ -237,6 +250,19 @@ module API
end
end
class MergeRequestDiff < Grape::Entity
expose :id, :head_commit_sha, :base_commit_sha, :start_commit_sha,
:created_at, :merge_request_id, :state, :real_size
end
class MergeRequestDiffFull < MergeRequestDiff
expose :commits, using: Entities::RepoCommit
expose :diffs, using: Entities::RepoDiff do |compare, _|
compare.raw_diffs(all_diffs: true).to_a
end
end
class SSHKey < Grape::Entity
expose :id, :title, :key, :created_at
end

View File

@ -279,6 +279,24 @@ module API
error!({ 'message' => message }, status)
end
def handle_api_exception(exception)
if sentry_enabled? && report_exception?(exception)
define_params_for_grape_middleware
sentry_context
Raven.capture_exception(exception)
end
# lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60
trace = exception.backtrace
message = "\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
API.logger.add Logger::FATAL, message
rack_response({ 'message' => '500 Internal Server Error' }.to_json, 500)
end
# Projects helpers
def filter_projects(projects)
@ -419,5 +437,19 @@ module API
Entities::Issue
end
end
# The Grape Error Middleware only has access to env but no params. We workaround this by
# defining a method that returns the right value.
def define_params_for_grape_middleware
self.define_singleton_method(:params) { Rack::Request.new(env).params.symbolize_keys }
end
# We could get a Grape or a standard Ruby exception. We should only report anything that
# is clearly an error.
def report_exception?(exception)
return true unless exception.respond_to?(:status)
exception.status == 500
end
end
end

View File

@ -101,6 +101,31 @@ module API
{}
end
end
post '/two_factor_recovery_codes' do
status 200
key = Key.find(params[:key_id])
user = key.user
# Make sure this isn't a deploy key
unless key.type.nil?
return { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
end
unless user.present?
return { success: false, message: 'Could not find a user for the given key' }
end
unless user.two_factor_enabled?
return { success: false, message: 'Two-factor authentication is not enabled for this user' }
end
codes = user.generate_otp_backup_codes!
user.save!
{ success: true, recovery_codes: codes }
end
end
end
end

View File

@ -0,0 +1,45 @@
module API
# MergeRequestDiff API
class MergeRequestDiffs < Grape::API
before { authenticate! }
resource :projects do
desc 'Get a list of merge request diff versions' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::MergeRequestDiff
end
params do
requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request'
end
get ":id/merge_requests/:merge_request_id/versions" do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff
end
desc 'Get a single merge request diff version' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::MergeRequestDiffFull
end
params do
requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request'
requires :version_id, type: Integer, desc: 'The ID of a merge request diff version'
end
get ":id/merge_requests/:merge_request_id/versions/:version_id" do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull
end
end
end
end

View File

@ -46,6 +46,7 @@ module API
:note_events,
:build_events,
:pipeline_events,
:wiki_page_events,
:enable_ssl_verification
]
@hook = user_project.hooks.new(attrs)
@ -80,6 +81,7 @@ module API
:note_events,
:build_events,
:pipeline_events,
:wiki_page_events,
:enable_ssl_verification
]

View File

@ -123,7 +123,8 @@ module API
:public,
:visibility_level,
:import_url,
:public_builds]
:public_builds,
:only_allow_merge_if_build_succeeds]
attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(current_user, attrs).execute
if @project.saved?
@ -172,7 +173,8 @@ module API
:public,
:visibility_level,
:import_url,
:public_builds]
:public_builds,
:only_allow_merge_if_build_succeeds]
attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(user, attrs).execute
if @project.saved?
@ -234,7 +236,8 @@ module API
:shared_runners_enabled,
:public,
:visibility_level,
:public_builds]
:public_builds,
:only_allow_merge_if_build_succeeds]
attrs = map_public_to_visibility_level(attrs)
authorize_admin_project
authorize! :rename_project, user_project if attrs[:name].present?

View File

@ -9,22 +9,14 @@ module Ci
end
rescue_from :all do |exception|
# lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60
# why is this not wrapped in something reusable?
trace = exception.backtrace
message = "\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
API.logger.add Logger::FATAL, message
rack_response({ 'message' => '500 Internal Server Error' }, 500)
handle_api_exception(exception)
end
content_type :txt, 'text/plain'
content_type :json, 'application/json'
format :json
helpers ::SentryHelper
helpers ::Ci::API::Helpers
helpers ::API::Helpers
helpers Gitlab::CurrentSettings

View File

@ -94,9 +94,7 @@ module ExtractsPath
@options = params.select {|key, value| allowed_options.include?(key) && !value.blank? }
@options = HashWithIndifferentAccess.new(@options)
@id = params[:id] || params[:ref]
@id += "/" + params[:path] unless params[:path].blank?
@id = get_id
@ref, @path = extract_ref(@id)
@repo = @project.repository
if @options[:extended_sha1].blank?
@ -118,4 +116,13 @@ module ExtractsPath
def tree
@tree ||= @repo.tree(@commit.id, @path)
end
private
# overriden in subclasses, do not remove
def get_id
id = params[:id] || params[:ref]
id += "/" + params[:path] unless params[:path].blank?
id
end
end

View File

@ -181,6 +181,17 @@ module Gitlab
sections: sections
}
end
# Don't try to print merge_request or repository.
def inspect
instance_variables = [:merge_file_result, :their_path, :our_path, :our_mode].map do |instance_variable|
value = instance_variable_get("@#{instance_variable}")
"#{instance_variable}=\"#{value}\""
end
"#<#{self.class} #{instance_variables.join(' ')}>"
end
end
end
end

View File

@ -13,10 +13,19 @@ module Gitlab
class UnmergeableFile < ParserError
end
class UnsupportedEncoding < ParserError
end
def parse(text, our_path:, their_path:, parent_file: nil)
raise UnmergeableFile if text.blank? # Typically a binary file
raise UnmergeableFile if text.length > 102400
begin
text.to_json
rescue Encoding::UndefinedConversionError
raise UnsupportedEncoding
end
line_obj_index = 0
line_old = 1
line_new = 1

View File

@ -23,7 +23,6 @@ module Gitlab
dates.each do |date|
date_id = date.to_time.to_i.to_s
@timestamps[date_id] = 0
day_events = events.find { |day_events| day_events["date"] == date }
if day_events

View File

@ -41,7 +41,7 @@ module Gitlab
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project],
import_sources: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,

View File

@ -1,14 +1,14 @@
module Gitlab
module Diff
module FileCollection
class MergeRequest < Base
def initialize(merge_request, diff_options:)
@merge_request = merge_request
class MergeRequestDiff < Base
def initialize(merge_request_diff, diff_options:)
@merge_request_diff = merge_request_diff
super(merge_request,
project: merge_request.project,
super(merge_request_diff,
project: merge_request_diff.project,
diff_options: diff_options,
diff_refs: merge_request.diff_refs)
diff_refs: merge_request_diff.diff_refs)
end
def diff_files
@ -61,11 +61,11 @@ module Gitlab
end
def cacheable?
@merge_request.merge_request_diff.present?
@merge_request_diff.present?
end
def cache_key
[@merge_request.merge_request_diff, 'highlighted-diff-files', diff_options]
[@merge_request_diff, 'highlighted-diff-files', diff_options]
end
end
end

View File

@ -3,12 +3,13 @@ module Gitlab
class Importer
include Gitlab::ShellAdapter
attr_reader :client, :project, :repo, :repo_url
attr_reader :client, :errors, :project, :repo, :repo_url
def initialize(project)
@project = project
@repo = project.import_source
@repo_url = project.import_url
@errors = []
if credentials
@client = Client.new(credentials[:user])
@ -18,8 +19,14 @@ module Gitlab
end
def execute
import_labels && import_milestones && import_issues &&
import_pull_requests && import_wiki
import_labels
import_milestones
import_issues
import_pull_requests
import_wiki
handle_errors
true
end
private
@ -28,22 +35,37 @@ module Gitlab
@credentials ||= project.import_data.credentials if project.import_data
end
def handle_errors
return unless errors.any?
project.update_column(:import_error, {
message: 'The remote data could not be fully imported.',
errors: errors
}.to_json)
end
def import_labels
labels = client.labels(repo, per_page: 100)
labels.each { |raw| LabelFormatter.new(project, raw).create! }
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
labels.each do |raw|
begin
LabelFormatter.new(project, raw).create!
rescue => e
errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
end
def import_milestones
milestones = client.milestones(repo, state: :all, per_page: 100)
milestones.each { |raw| MilestoneFormatter.new(project, raw).create! }
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
milestones.each do |raw|
begin
MilestoneFormatter.new(project, raw).create!
rescue => e
errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
end
def import_issues
@ -53,15 +75,15 @@ module Gitlab
gh_issue = IssueFormatter.new(project, raw)
if gh_issue.valid?
issue = gh_issue.create!
apply_labels(issue)
import_comments(issue) if gh_issue.has_comments?
begin
issue = gh_issue.create!
apply_labels(issue)
import_comments(issue) if gh_issue.has_comments?
rescue => e
errors << { type: :issue, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
end
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
end
def import_pull_requests
@ -77,14 +99,12 @@ module Gitlab
apply_labels(merge_request)
import_comments(merge_request)
import_comments_on_diff(merge_request)
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
rescue => e
errors << { type: :pull_request, url: Gitlab::UrlSanitizer.sanitize(pull_request.url), errors: e.message }
ensure
clean_up_restored_branches(pull_request)
end
end
true
end
def restore_source_branch(pull_request)
@ -98,7 +118,7 @@ module Gitlab
def remove_branch(name)
project.repository.delete_branch(name)
rescue Rugged::ReferenceError
nil
errors << { type: :remove_branch, name: name }
end
def clean_up_restored_branches(pull_request)
@ -112,9 +132,10 @@ module Gitlab
issue = client.issue(repo, issuable.iid)
if issue.labels.count > 0
label_ids = issue.labels.map do |raw|
Label.find_by(LabelFormatter.new(project, raw).attributes).try(:id)
end
label_ids = issue.labels
.map { |raw| LabelFormatter.new(project, raw).attributes }
.map { |attrs| Label.find_by(attrs).try(:id) }
.compact
issuable.update_attribute(:label_ids, label_ids)
end
@ -132,8 +153,12 @@ module Gitlab
def create_comments(issuable, comments)
comments.each do |raw|
comment = CommentFormatter.new(project, raw)
issuable.notes.create!(comment.attributes)
begin
comment = CommentFormatter.new(project, raw)
issuable.notes.create!(comment.attributes)
rescue => e
errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
end
@ -143,16 +168,12 @@ module Gitlab
gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url)
project.update_attribute(:wiki_enabled, true)
end
true
rescue Gitlab::Shell::Error => e
# GitHub error message when the wiki repo has not been created,
# this means that repo has wiki enabled, but have no pages. So,
# we can skip the import.
if e.message !~ /repository not exported/
raise Projects::ImportService::Error, e.message
else
true
errors << { type: :wiki, errors: e.message }
end
end
end

View File

@ -56,6 +56,10 @@ module Gitlab
end
end
def url
raw_data.url
end
private
def assigned?

View File

@ -1,5 +0,0 @@
module Gitlab
module GitoriousImport
GITORIOUS_HOST = "https://gitorious.org"
end
end

View File

@ -1,29 +0,0 @@
module Gitlab
module GitoriousImport
class Client
attr_reader :repo_list
def initialize(repo_list)
@repo_list = repo_list
end
def authorize_url(redirect_uri)
"#{GITORIOUS_HOST}/gitlab-import?callback_url=#{redirect_uri}"
end
def repos
@repos ||= repo_names.map { |full_name| GitoriousImport::Repository.new(full_name) }
end
def repo(id)
repos.find { |repo| repo.id == id }
end
private
def repo_names
repo_list.to_s.split(',').map(&:strip).reject(&:blank?)
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More