Commit 841a5ef5 authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into sh-headless-chrome-support

parents 3e75b7fa 84336b84
...@@ -16,7 +16,7 @@ gem 'mysql2', '~> 0.4.5', group: :mysql ...@@ -16,7 +16,7 @@ gem 'mysql2', '~> 0.4.5', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.26.0' gem 'rugged', '~> 0.26.0'
gem 'grape-route-helpers', '~> 2.0.0' gem 'grape-route-helpers', '~> 2.1.0'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
...@@ -76,7 +76,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false ...@@ -76,7 +76,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.19.2' gem 'grape', '~> 1.0'
gem 'grape-entity', '~> 0.6.0' gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
...@@ -144,8 +144,6 @@ end ...@@ -144,8 +144,6 @@ end
# State machine # State machine
gem 'state_machines-activerecord', '~> 0.4.0' gem 'state_machines-activerecord', '~> 0.4.0'
# Run events after state machine commits
gem 'after_commit_queue', '~> 1.3.0'
# Issue tags # Issue tags
gem 'acts-as-taggable-on', '~> 4.0' gem 'acts-as-taggable-on', '~> 4.0'
...@@ -289,7 +287,7 @@ group :metrics do ...@@ -289,7 +287,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false gem 'influxdb', '~> 0.2', require: false
# Prometheus # Prometheus
gem 'prometheus-client-mmap', '~>0.7.0.beta11' gem 'prometheus-client-mmap', '~>0.7.0.beta12'
gem 'raindrops', '~> 0.18' gem 'raindrops', '~> 0.18'
end end
...@@ -403,7 +401,7 @@ group :ed25519 do ...@@ -403,7 +401,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.27.0' gem 'gitaly', '~> 0.29.0'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -46,8 +46,6 @@ GEM ...@@ -46,8 +46,6 @@ GEM
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
memoizable (~> 0.4.0) memoizable (~> 0.4.0)
addressable (2.3.8) addressable (2.3.8)
after_commit_queue (1.3.0)
activerecord (>= 3.0)
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.5) allocations (1.0.5)
arel (6.0.4) arel (6.0.4)
...@@ -278,7 +276,7 @@ GEM ...@@ -278,7 +276,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.27.0) gitaly (0.29.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -343,12 +341,9 @@ GEM ...@@ -343,12 +341,9 @@ GEM
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.13)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.1)
grape (0.19.2) grape (1.0.0)
activesupport activesupport
builder builder
hashie (>= 2.1.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
mustermann-grape (~> 1.0.0) mustermann-grape (~> 1.0.0)
rack (>= 1.3.0) rack (>= 1.3.0)
rack-accept rack-accept
...@@ -356,11 +351,11 @@ GEM ...@@ -356,11 +351,11 @@ GEM
grape-entity (0.6.0) grape-entity (0.6.0)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-route-helpers (2.0.0) grape-route-helpers (2.1.0)
activesupport activesupport
grape (~> 0.16, >= 0.16.0) grape (>= 0.16.0)
rake rake
grpc (1.4.0) grpc (1.4.5)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
haml (4.0.7) haml (4.0.7)
...@@ -376,7 +371,7 @@ GEM ...@@ -376,7 +371,7 @@ GEM
thor thor
tilt tilt
hashdiff (0.3.4) hashdiff (0.3.4)
hashie (3.5.5) hashie (3.5.6)
hashie-forbidden_attributes (0.1.1) hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0) hashie (>= 3.0)
health_check (2.6.0) health_check (2.6.0)
...@@ -621,7 +616,7 @@ GEM ...@@ -621,7 +616,7 @@ GEM
parser parser
unparser unparser
procto (0.0.3) procto (0.0.3)
prometheus-client-mmap (0.7.0.beta11) prometheus-client-mmap (0.7.0.beta12)
mmap2 (~> 2.2, >= 2.2.7) mmap2 (~> 2.2, >= 2.2.7)
pry (0.10.4) pry (0.10.4)
coderay (~> 1.1.0) coderay (~> 1.1.0)
...@@ -960,7 +955,6 @@ DEPENDENCIES ...@@ -960,7 +955,6 @@ DEPENDENCIES
activerecord_sane_schema_dumper (= 0.2) activerecord_sane_schema_dumper (= 0.2)
acts-as-taggable-on (~> 4.0) acts-as-taggable-on (~> 4.0)
addressable (~> 2.3.8) addressable (~> 2.3.8)
after_commit_queue (~> 1.3.0)
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.6.0) asana (~> 0.6.0)
...@@ -1022,7 +1016,7 @@ DEPENDENCIES ...@@ -1022,7 +1016,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.27.0) gitaly (~> 0.29.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
...@@ -1032,9 +1026,9 @@ DEPENDENCIES ...@@ -1032,9 +1026,9 @@ DEPENDENCIES
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
gpgme gpgme
grape (~> 0.19.2) grape (~> 1.0)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.0.0) grape-route-helpers (~> 2.1.0)
haml_lint (~> 0.26.0) haml_lint (~> 0.26.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
hashie-forbidden_attributes hashie-forbidden_attributes
...@@ -1096,7 +1090,7 @@ DEPENDENCIES ...@@ -1096,7 +1090,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3) peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2) pg (~> 0.18.2)
premailer-rails (~> 1.9.7) premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta11) prometheus-client-mmap (~> 0.7.0.beta12)
pry-byebug (~> 3.4.1) pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
......
...@@ -414,7 +414,7 @@ import initChangesDropdown from './init_changes_dropdown'; ...@@ -414,7 +414,7 @@ import initChangesDropdown from './init_changes_dropdown';
case 'projects:tree:show': case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
if (UserFeatureHelper.isNewRepo()) break; if (UserFeatureHelper.isNewRepoEnabled()) break;
new TreeView(); new TreeView();
new BlobViewer(); new BlobViewer();
...@@ -434,7 +434,7 @@ import initChangesDropdown from './init_changes_dropdown'; ...@@ -434,7 +434,7 @@ import initChangesDropdown from './init_changes_dropdown';
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:blob:show': case 'projects:blob:show':
if (UserFeatureHelper.isNewRepo()) break; if (UserFeatureHelper.isNewRepoEnabled()) break;
new BlobViewer(); new BlobViewer();
initBlob(); initBlob();
break; break;
......
...@@ -111,8 +111,7 @@ window.GroupsSelect = (function() { ...@@ -111,8 +111,7 @@ window.GroupsSelect = (function() {
}; };
GroupsSelect.prototype.forceOverflow = function (e) { GroupsSelect.prototype.forceOverflow = function (e) {
const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight; this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight)}px`;
this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`;
}; };
GroupsSelect.PER_PAGE = 20; GroupsSelect.PER_PAGE = 20;
......
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
function isNewRepo() { export default {
return Cookies.get('new_repo') === 'true'; isNewRepoEnabled() {
} return Cookies.get('new_repo') === 'true';
},
const UserFeatureHelper = {
isNewRepo,
}; };
export default UserFeatureHelper;
...@@ -378,15 +378,15 @@ ...@@ -378,15 +378,15 @@
w.gl.utils.backOff = (fn, timeout = 60000) => { w.gl.utils.backOff = (fn, timeout = 60000) => {
const maxInterval = 32000; const maxInterval = 32000;
let nextInterval = 2000; let nextInterval = 2000;
let timeElapsed = 0;
const startTime = Date.now();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg)); const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
const next = () => { const next = () => {
if (Date.now() - startTime < timeout) { if (timeElapsed < timeout) {
setTimeout(fn.bind(null, next, stop), nextInterval); setTimeout(() => fn(next, stop), nextInterval);
timeElapsed += nextInterval;
nextInterval = Math.min(nextInterval + nextInterval, maxInterval); nextInterval = Math.min(nextInterval + nextInterval, maxInterval);
} else { } else {
reject(new Error('BACKOFF_TIMEOUT')); reject(new Error('BACKOFF_TIMEOUT'));
......
...@@ -74,7 +74,8 @@ export default { ...@@ -74,7 +74,8 @@ export default {
<tbody> <tbody>
<repo-file-options <repo-file-options
:is-mini="isMini" :is-mini="isMini"
:project-name="projectName"/> :project-name="projectName"
/>
<repo-previous-directory <repo-previous-directory
v-if="isRoot" v-if="isRoot"
:prev-url="prevURL" :prev-url="prevURL"
...@@ -84,7 +85,8 @@ export default { ...@@ -84,7 +85,8 @@ export default {
:key="n" :key="n"
:loading="loading" :loading="loading"
:has-files="!!files.length" :has-files="!!files.length"
:is-mini="isMini"/> :is-mini="isMini"
/>
<repo-file <repo-file
v-for="file in files" v-for="file in files"
:key="file.id" :key="file.id"
...@@ -93,7 +95,8 @@ export default { ...@@ -93,7 +95,8 @@ export default {
@linkclicked="fileClicked(file)" @linkclicked="fileClicked(file)"
:is-tree="isTree" :is-tree="isTree"
:has-files="!!files.length" :has-files="!!files.length"
:active-file="activeFile"/> :active-file="activeFile"
/>
</tbody> </tbody>
</table> </table>
</div> </div>
......
...@@ -728,26 +728,41 @@ ...@@ -728,26 +728,41 @@
@mixin new-style-dropdown($selector: '') { @mixin new-style-dropdown($selector: '') {
#{$selector}.dropdown-menu, #{$selector}.dropdown-menu,
#{$selector}.dropdown-menu-nav { #{$selector}.dropdown-menu-nav {
.divider {
margin: 6px 0;
}
li { li {
padding: 0 1px; padding: 0 1px;
&:hover {
background-color: transparent;
}
&.divider {
margin: 6px 0;
&:hover {
background-color: $dropdown-divider-color;
}
}
&.dropdown-header { &.dropdown-header {
padding: 8px 16px; padding: 8px 16px;
} }
a { a,
button {
border-radius: 0; border-radius: 0;
padding: 8px 16px; padding: 8px 16px;
// make sure the text color is not overriden
&.text-danger {
@extend .text-danger;
}
&.is-focused, &.is-focused,
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
background-color: $gray-darker; background-color: $gray-darker;
color: $gl-text-color;
} }
&.is-active { &.is-active {
......
...@@ -50,6 +50,8 @@ ...@@ -50,6 +50,8 @@
} }
.filtered-search-wrapper { .filtered-search-wrapper {
@include new-style-dropdown;
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
...@@ -411,8 +413,6 @@ ...@@ -411,8 +413,6 @@
} }
%filter-dropdown-item-btn-hover { %filter-dropdown-item-btn-hover {
background-color: $dropdown-hover-color;
color: $white-light;
text-decoration: none; text-decoration: none;
outline: 0; outline: 0;
...@@ -422,8 +422,6 @@ ...@@ -422,8 +422,6 @@
} }
.droplab-dropdown .dropdown-menu .filter-dropdown-item { .droplab-dropdown .dropdown-menu .filter-dropdown-item {
padding: 0;
.btn { .btn {
border: none; border: none;
width: 100%; width: 100%;
......
...@@ -132,6 +132,8 @@ ul.content-list { ...@@ -132,6 +132,8 @@ ul.content-list {
} }
.controls { .controls {
@include new-style-dropdown;
float: right; float: right;
> .control-text { > .control-text {
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
background-color: $gray-lightest; background-color: $gray-lightest;
} }
img.js-lazy-loaded { img.js-lazy-loaded,
img.emoji {
min-width: inherit; min-width: inherit;
min-height: inherit; min-height: inherit;
background-color: inherit; background-color: inherit;
......
...@@ -103,7 +103,10 @@ $new-sidebar-collapsed-width: 50px; ...@@ -103,7 +103,10 @@ $new-sidebar-collapsed-width: 50px;
&.sidebar-icons-only { &.sidebar-icons-only {
width: $new-sidebar-collapsed-width; width: $new-sidebar-collapsed-width;
overflow-x: hidden;
.nav-sidebar-inner-scroll {
overflow-x: hidden;
}
.badge, .badge,
.project-title { .project-title {
...@@ -111,7 +114,11 @@ $new-sidebar-collapsed-width: 50px; ...@@ -111,7 +114,11 @@ $new-sidebar-collapsed-width: 50px;
} }
.nav-item-name { .nav-item-name {
opacity: 0; display: none;
}
.sidebar-top-level-items > li > a {
min-height: 44px;
} }
} }
......
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
padding-top: 10px; padding-top: 10px;
} }
&:not(.issue-boards-sidebar):not([data-signed-in]) { &:not(.issue-boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header { .issuable-sidebar-header {
display: none; display: none;
} }
......
...@@ -35,18 +35,18 @@ module EventsHelper ...@@ -35,18 +35,18 @@ module EventsHelper
[event.action_name, target].join(" ") [event.action_name, target].join(" ")
end end
def event_filter_link(key, tooltip) def event_filter_link(key, text, tooltip)
key = key.to_s key = key.to_s
active = 'active' if @event_filter.active?(key) active = 'active' if @event_filter.active?(key)
link_opts = { link_opts = {
class: "event-filter-link", class: "event-filter-link has-tooltip",
id: "#{key}_event_filter", id: "#{key}_event_filter",
title: "Filter by #{tooltip.downcase}" title: tooltip
} }
content_tag :li, class: active do content_tag :li, class: active do
link_to request.path, link_opts do link_to request.path, link_opts do
content_tag(:span, ' ' + tooltip) content_tag(:span, ' ' + text)
end end
end end
end end
......
...@@ -9,7 +9,7 @@ module BlobViewer ...@@ -9,7 +9,7 @@ module BlobViewer
end end
def manager_url def manager_url
'https://getcomposer.com/' 'https://getcomposer.org/'
end end
def package_name def package_name
......
...@@ -25,6 +25,11 @@ module Referable ...@@ -25,6 +25,11 @@ module Referable
to_reference(from_project) to_reference(from_project)
end end
included do
alias_method :non_referable_inspect, :inspect
alias_method :inspect, :referable_inspect
end
def referable_inspect def referable_inspect
if respond_to?(:id) if respond_to?(:id)
"#<#{self.class.name} id:#{id} #{to_reference(full: true)}>" "#<#{self.class.name} id:#{id} #{to_reference(full: true)}>"
...@@ -33,10 +38,6 @@ module Referable ...@@ -33,10 +38,6 @@ module Referable
end end
end end
def inspect
referable_inspect
end
module ClassMethods module ClassMethods
# The character that prefixes the actual reference identifier # The character that prefixes the actual reference identifier
# #
......
...@@ -60,7 +60,7 @@ class Project < ActiveRecord::Base ...@@ -60,7 +60,7 @@ class Project < ActiveRecord::Base
end end
before_destroy :remove_private_deploy_keys before_destroy :remove_private_deploy_keys
after_destroy :remove_pages after_destroy -> { run_after_commit { remove_pages } }
# update visibility_level of forks # update visibility_level of forks
after_update :update_forks_visibility_level after_update :update_forks_visibility_level
...@@ -369,7 +369,10 @@ class Project < ActiveRecord::Base ...@@ -369,7 +369,10 @@ class Project < ActiveRecord::Base
state :failed state :failed
after_transition [:none, :finished, :failed] => :scheduled do |project, _| after_transition [:none, :finished, :failed] => :scheduled do |project, _|
project.run_after_commit { add_import_job } project.run_after_commit do
job_id = add_import_job
update(import_jid: job_id) if job_id
end
end end
after_transition started: :finished do |project, _| after_transition started: :finished do |project, _|
...@@ -524,17 +527,26 @@ class Project < ActiveRecord::Base ...@@ -524,17 +527,26 @@ class Project < ActiveRecord::Base
def add_import_job def add_import_job
job_id = job_id =
if forked? if forked?
RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, RepositoryForkWorker.perform_async(id,
forked_from_project.full_path, forked_from_project.repository_storage_path,
self.namespace.full_path) forked_from_project.full_path,
self.namespace.full_path)
else else
RepositoryImportWorker.perform_async(self.id) RepositoryImportWorker.perform_async(self.id)
end end
log_import_activity(job_id)
job_id
end
def log_import_activity(job_id, type: :import)
job_type = type.to_s.capitalize
if job_id if job_id
Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}" Rails.logger.info("#{job_type} job scheduled for #{full_path} with job ID #{job_id}.")
else else
Rails.logger.error "Import job failed to start for #{full_path}" Rails.logger.error("#{job_type} job failed to create for #{full_path}.")
end end
end end
...@@ -543,6 +555,7 @@ class Project < ActiveRecord::Base ...@@ -543,6 +555,7 @@ class Project < ActiveRecord::Base
ProjectCacheWorker.perform_async(self.id) ProjectCacheWorker.perform_async(self.id)
end end
update(import_error: nil)
remove_import_data remove_import_data
end end
...@@ -1224,6 +1237,9 @@ class Project < ActiveRecord::Base ...@@ -1224,6 +1237,9 @@ class Project < ActiveRecord::Base
# TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal? # TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
def remove_pages def remove_pages
# Projects with a missing namespace cannot have their pages removed
return unless namespace
::Projects::UpdatePagesConfigurationService.new(self).execute ::Projects::UpdatePagesConfigurationService.new(self).execute
# 1. We rename pages to temporary directory # 1. We rename pages to temporary directory
......
...@@ -47,11 +47,6 @@ class User < ActiveRecord::Base ...@@ -47,11 +47,6 @@ class User < ActiveRecord::Base
devise :lockable, :recoverable, :rememberable, :trackable, devise :lockable, :recoverable, :rememberable, :trackable,
:validatable, :omniauthable, :confirmable, :registerable :validatable, :omniauthable, :confirmable, :registerable
# devise overrides #inspect, so we manually use the Referable one
def inspect
referable_inspect
end
# Override Devise::Models::Trackable#update_tracked_fields! # Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour # to limit database writes to at most once every hour
def update_tracked_fields!(request) def update_tracked_fields!(request)
......
...@@ -40,9 +40,10 @@ ...@@ -40,9 +40,10 @@
%li %li
= link_to 'Remove user', admin_user_path(user), = link_to 'Remove user', admin_user_path(user),
data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" },
class: 'text-danger',
method: :delete method: :delete
%li %li
= link_to 'Remove user and contributions', admin_user_path(user, hard_delete: true), = link_to 'Remove user and contributions', admin_user_path(user, hard_delete: true),
data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and comments authored by this user, and groups owned solely by them, will also be removed! Are you sure?" }, data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and comments authored by this user, and groups owned solely by them, will also be removed! Are you sure?" },
class: 'btn btn-remove btn-block', class: 'text-danger',
method: :delete method: :delete
...@@ -92,25 +92,24 @@ ...@@ -92,25 +92,24 @@
Update username Update username
%hr %hr
- if signup_enabled? .row.prepend-top-default
.row.prepend-top-default .col-lg-4.profile-settings-sidebar
.col-lg-4.profile-settings-sidebar %h4.prepend-top-0.danger-title
%h4.prepend-top-0.danger-title Remove account
Remove account .col-lg-8
.col-lg-8 - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
- if @user.can_be_removed? && can?(current_user, :destroy_user, @user) %p
Deleting an account has the following effects:
= render 'users/deletion_guidance', user: current_user
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- else
- if @user.solo_owned_groups.present?
%p %p
Deleting an account has the following effects: Your account is currently an owner in these groups:
= render 'users/deletion_guidance', user: current_user %strong= @user.solo_owned_groups.map(&:name).join(', ')
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" %p
You must transfer ownership or delete these groups before you can delete your account.
- else - else
- if @user.solo_owned_groups.present? %p
%p You don't have access to delete this user.
Your account is currently an owner in these groups:
%strong= @user.solo_owned_groups.map(&:name).join(', ')
%p
You must transfer ownership or delete these groups before you can delete your account.
- else
%p
You don't have access to delete this user.
.append-bottom-default .append-bottom-default
%div{ class: container_class } %div{ class: container_class }
.nav-block.activity-filter-block.activities .nav-block.activity-filter-block.activities
.controls .controls
= link_to project_path(@project, rss_url_options), title: "Subscribe", class: 'btn rss-btn has-tooltip' do = link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn rss-btn has-tooltip' do
= icon('rss') = icon('rss')
= render 'shared/event_filter' = render 'shared/event_filter'
......
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
.row-content-block.top-block.hidden-xs.white .row-content-block.top-block.hidden-xs.white
.event-last-push .event-last-push
.event-last-push-text .event-last-push-text
%span You pushed to %span= s_("LastPushEvent|You pushed to")
%strong %strong
= link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name' = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name'
- if event.project != @project - if event.project != @project
%span at %span= s_("LastPushEvent|at")
%strong= link_to_project event.project %strong= link_to_project event.project
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
.pull-right .pull-right
= link_to new_mr_path_from_push_event(event), title: "New merge request", class: "btn btn-info btn-sm" do = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do
#{ _('Create merge request') } #{ _('Create merge request') }
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
Blank Blank
- Gitlab::ProjectTemplate.all.each do |template| - Gitlab::ProjectTemplate.all.each do |template|
.btn .btn
%input{ type: "radio", autocomplete: "off", name: "project_templates", id: template.name } %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: template.name, value: template.name }
= custom_icon(template.logo) = custom_icon(template.logo)
= template.title = template.title
- @no_container = true - @no_container = true
- if show_new_nav? - if show_new_nav?
- add_to_breadcrumbs("Project", project_path(@project)) - add_to_breadcrumbs(_("Project"), project_path(@project))
- page_title "Activity" - page_title _("Activity")
= render "projects/head" = render "projects/head"
= render 'projects/last_push' = render 'projects/last_push'
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- notes = commit.notes - notes = commit.notes
- note_count = notes.user.count - note_count = notes.user.count
- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)] - cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits), I18n.locale]
- cache_key.push(commit.status(ref)) if commit.status(ref) - cache_key.push(commit.status(ref)) if commit.status(ref)
= cache(cache_key, expires_in: 1.day) do = cache(cache_key, expires_in: 1.day) do
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
.form-group .form-group
= f.label :template_project, class: 'label-light' do = f.label :template_project, class: 'label-light' do
Create from template Create from template
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'} = link_to icon('question-circle'), help_page_path("gitlab-basics/create-project"), target: '_blank', aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'}
%div %div
= render 'project_templates', f: f = render 'project_templates', f: f
.second-column .second-column
......
%ul.nav-links.event-filter.scrolling-tabs %ul.nav-links.event-filter.scrolling-tabs
= event_filter_link EventFilter.all, 'All' = event_filter_link EventFilter.all, _('All'), s_('EventFilterBy|Filter by all')
- if event_filter_visible(:repository) - if event_filter_visible(:repository)
= event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.push, _('Push events'), s_('EventFilterBy|Filter by push events')
- if event_filter_visible(:merge_requests) - if event_filter_visible(:merge_requests)
= event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.merged, _('Merge events'), s_('EventFilterBy|Filter by merge events')
- if event_filter_visible(:issues) - if event_filter_visible(:issues)
= event_filter_link EventFilter.issue, 'Issue events' = event_filter_link EventFilter.issue, _('Issue events'), s_('EventFilterBy|Filter by issue events')
- if comments_visible? - if comments_visible?
= event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.comments, _('Comments'), s_('EventFilterBy|Filter by comments')
= event_filter_link EventFilter.team, 'Team' = event_filter_link EventFilter.team, _('Team'), s_('EventFilterBy|Filter by team')
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
%li %li
The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination. The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination.
%li %li
To migrate an SVN repository, check out #{link_to "this document", help_page_path('workflow/importing/migrating_from_svn')}. To migrate an SVN repository, check out #{link_to "this document", help_page_path('user/project/import/svn')}.
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link %button.btn.btn-link
No Assignee No Assignee
%li.divider %li.divider.droplab-item-ignore
- if current_user - if current_user
= render 'shared/issuable/user_dropdown_item', = render 'shared/issuable/user_dropdown_item',
user: current_user user: current_user
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
%li.filter-dropdown-item{ 'data-value' => 'started' } %li.filter-dropdown-item{ 'data-value' => 'started' }
%button.btn.btn-link %button.btn.btn-link
Started Started
%li.divider %li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link.js-data-value %button.btn.btn-link.js-data-value
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link %button.btn.btn-link
No Label No Label
%li.divider %li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link %button.btn.btn-link
......
- affix_offset = local_assigns.fetch(:affix_offset, "50") - affix_offset = local_assigns.fetch(:affix_offset, "50")
- project = local_assigns[:project] - project = local_assigns[:project]
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar.milestone-sidebar .issuable-sidebar.milestone-sidebar
.block.milestone-progress.issuable-sidebar-header .block.milestone-progress.issuable-sidebar-header
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" } %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
......
...@@ -24,10 +24,6 @@ class NamespacelessProjectDestroyWorker ...@@ -24,10 +24,6 @@ class NamespacelessProjectDestroyWorker
unlink_fork(project) if project.forked? unlink_fork(project) if project.forked?
# Override Project#remove_pages for this instance so it doesn't do anything
def project.remove_pages
end
project.destroy! project.destroy!
end end
......
...@@ -5,14 +5,17 @@ class RepositoryForkWorker ...@@ -5,14 +5,17 @@ class RepositoryForkWorker
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
def perform(project_id, forked_from_repository_storage_path, source_path, target_path) def perform(project_id, forked_from_repository_storage_path, source_path, target_path)
project = Project.find(project_id)
return unless start_fork(project)
Gitlab::Metrics.add_event(:fork_repository, Gitlab::Metrics.add_event(:fork_repository,
source_path: source_path, source_path: source_path,
target_path: target_path) target_path: target_path)
project = Project.find(project_id)
project.import_start
result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path, result = gitlab_shell.fork_repository(forked_from_repository_storage_path, source_path,
project.repository_storage_path, target_path) project.repository_storage_path, target_path)
raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result raise ForkError, "Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}" unless result
...@@ -33,6 +36,13 @@ class RepositoryForkWorker ...@@ -33,6 +36,13 @@ class RepositoryForkWorker
private private
def start_fork(project)
return true if project.import_start
Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while forking.")
false
end
def fail_fork(project, message) def fail_fork(project, message)
Rails.logger.error(message) Rails.logger.error(message)
project.mark_import_as_failed(message) project.mark_import_as_failed(message)
......
...@@ -4,23 +4,18 @@ class RepositoryImportWorker ...@@ -4,23 +4,18 @@ class RepositoryImportWorker
include Sidekiq::Worker include Sidekiq::Worker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_EXPIRATION sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
attr_accessor :project, :current_user
def perform(project_id) def perform(project_id)
@project = Project.find(project_id) project = Project.find(project_id)
@current_user = @project.creator
project.import_start return unless start_import(project)
Gitlab::Metrics.add_event(:import_repository, Gitlab::Metrics.add_event(:import_repository,
import_url: @project.import_url, import_url: project.import_url,
path: @project.full_path) path: project.full_path)
project.update_columns(import_jid: self.jid, import_error: nil)
result = Projects::ImportService.new(project, current_user).execute result = Projects::ImportService.new(project, project.creator).execute
raise ImportError, result[:message] if result[:status] == :error raise ImportError, result[:message] if result[:status] == :error
project.repository.after_import project.repository.after_import
...@@ -37,6 +32,13 @@ class RepositoryImportWorker ...@@ -37,6 +32,13 @@ class RepositoryImportWorker
private private
def start_import(project)
return true if project.import_start
Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.")
false
end
def fail_import(project, message) def fail_import(project, message)
project.mark_import_as_failed(message) project.mark_import_as_failed(message)
end end
......
...@@ -2,36 +2,60 @@ class StuckImportJobsWorker ...@@ -2,36 +2,60 @@ class StuckImportJobsWorker
include Sidekiq::Worker include Sidekiq::Worker
include CronjobQueue include CronjobQueue
IMPORT_EXPIRATION = 15.hours.to_i IMPORT_JOBS_EXPIRATION = 15.hours.to_i
def perform def perform
stuck_projects.find_in_batches(batch_size: 500) do |group| projects_without_jid_count = mark_projects_without_jid_as_failed!
projects_with_jid_count = mark_projects_with_jid_as_failed!
Gitlab::Metrics.add_event(:stuck_import_jobs,
projects_without_jid_count: projects_without_jid_count,
projects_with_jid_count: projects_with_jid_count)
end
private
def mark_projects_without_jid_as_failed!
started_projects_without_jid.each do |project|
project.mark_import_as_failed(error_message)
end.count
end
def mark_projects_with_jid_as_failed!
completed_jids_count = 0
started_projects_with_jid.find_in_batches(batch_size: 500) do |group|
jids = group.map(&:import_jid) jids = group.map(&:import_jid)
# Find the jobs that aren't currently running or that exceeded the threshold. # Find the jobs that aren't currently running or that exceeded the threshold.
completed_jids = Gitlab::SidekiqStatus.completed_jids(jids) completed_jids = Gitlab::SidekiqStatus.completed_jids(jids).to_set
if completed_jids.any? if completed_jids.any?
completed_ids = group.select { |project| completed_jids.include?(project.import_jid) }.map(&:id) completed_jids_count += completed_jids.count
group.each do |project|
project.mark_import_as_failed(error_message) if completed_jids.include?(project.import_jid)
end
fail_batch!(completed_jids, completed_ids) Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.to_a.join(', ')}")
end end
end end
end
private completed_jids_count
end
def stuck_projects def started_projects
Project.select('id, import_jid').with_import_status(:started).where.not(import_jid: nil) Project.with_import_status(:started)
end end
def fail_batch!(completed_jids, completed_ids) def started_projects_with_jid
Project.where(id: completed_ids).update_all(import_status: 'failed', import_error: error_message) started_projects.where.not(import_jid: nil)
end
Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.join(', ')}") def started_projects_without_jid
started_projects.where(import_jid: nil)
end end
def error_message def error_message
"Import timed out. Import took longer than #{IMPORT_EXPIRATION} seconds" "Import timed out. Import took longer than #{IMPORT_JOBS_EXPIRATION} seconds"
end end
end end
---
title: Fix deleting GitLab Pages files when a project is removed
merge_request: 13631
author:
type: fixed
---
title: Don't escape html entities in InlineDiffMarkdownMarker
merge_request:
author:
---
title: allow all users to delete their account
merge_request: 13636
author: Jacopo Beschi @jacopo-beschi
type: changed
---
title: Fix external link to Composer website
merge_request:
author:
type: fixed
---
title: Commit rows would occasionally render with the wrong language
merge_request:
author:
type: fixed
---
title: Implement the Gitaly RefService::RefExists endpoint
merge_request: 13528
author: Andrew Newdigate
---
title: Fix project milestones import when projects belongs to a group
merge_request:
author:
---
title: Improve API pagination headers when no record found
merge_request: 13629
author: Jordan Patterson
type: fixed
---
title: Upgrade grape to 1.0
merge_request:
author:
type: other
...@@ -139,6 +139,8 @@ if Settings.ldap['enabled'] || Rails.env.test? ...@@ -139,6 +139,8 @@ if Settings.ldap['enabled'] || Rails.env.test?
end end
Settings.ldap['servers'].each do |key, server| Settings.ldap['servers'].each do |key, server|
server = Settingslogic.new(server)
server['label'] ||= 'LDAP' server['label'] ||= 'LDAP'
server['timeout'] ||= 10.seconds server['timeout'] ||= 10.seconds
server['block_auto_created_users'] = false if server['block_auto_created_users'].nil? server['block_auto_created_users'] = false if server['block_auto_created_users'].nil?
...@@ -165,6 +167,8 @@ if Settings.ldap['enabled'] || Rails.env.test? ...@@ -165,6 +167,8 @@ if Settings.ldap['enabled'] || Rails.env.test?
MSG MSG
Rails.logger.warn(message) Rails.logger.warn(message)
end end
Settings.ldap['servers'][key] = server
end end
end end
...@@ -436,7 +440,9 @@ unless Settings.repositories.storages['default'] ...@@ -436,7 +440,9 @@ unless Settings.repositories.storages['default']
Settings.repositories.storages['default']['path'] ||= Settings.gitlab['user_home'] + '/repositories/' Settings.repositories.storages['default']['path'] ||= Settings.gitlab['user_home'] + '/repositories/'
end end
Settings.repositories.storages.values.each do |storage| Settings.repositories.storages.each do |key, storage|
storage = Settingslogic.new(storage)
# Expand relative paths # Expand relative paths
storage['path'] = Settings.absolute(storage['path']) storage['path'] = Settings.absolute(storage['path'])
# Set failure defaults # Set failure defaults
...@@ -450,6 +456,8 @@ Settings.repositories.storages.values.each do |storage| ...@@ -450,6 +456,8 @@ Settings.repositories.storages.values.each do |storage|
storage['failure_reset_time'] = storage['failure_reset_time'].to_i storage['failure_reset_time'] = storage['failure_reset_time'].to_i
# We might want to have a timeout shorter than 1 second. # We might want to have a timeout shorter than 1 second.
storage['storage_timeout'] = storage['storage_timeout'].to_f storage['storage_timeout'] = storage['storage_timeout'].to_f
Settings.repositories.storages[key] = storage
end end
# #
......
...@@ -102,7 +102,7 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i ...@@ -102,7 +102,7 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i
### Migrate and import your projects from other platforms ### Migrate and import your projects from other platforms
- [Importing to GitLab](workflow/importing/README.md): Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab. - [Importing to GitLab](user/project/import/index.md): Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab.
- [Migrating from SVN](workflow/importing/migrating_from_svn.md): Convert a SVN repository to Git and GitLab. - [Migrating from SVN](workflow/importing/migrating_from_svn.md): Convert a SVN repository to Git and GitLab.
### Continuous Integration, Delivery, and Deployment ### Continuous Integration, Delivery, and Deployment
......
# Group-level Variables API # Group-level Variables API
> [Introduced][ce-34519] in GitLab 9.5
## List group variables ## List group variables
Get list of a group's variables. Get list of a group's variables.
...@@ -123,3 +125,5 @@ DELETE /groups/:id/variables/:key ...@@ -123,3 +125,5 @@ DELETE /groups/:id/variables/:key
``` ```
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/VARIABLE_1" curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/groups/1/variables/VARIABLE_1"
``` ```
[ce-34519]: https://gitlab.com/gitlab-org/gitlab-ce/issues/34519
...@@ -150,4 +150,4 @@ Example response: ...@@ -150,4 +150,4 @@ Example response:
} }
``` ```
[ce-[ce-29508]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29508]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29508 [ce-29508]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29508
This diff is collapsed.
This diff is collapsed.
...@@ -117,7 +117,7 @@ following these rules: ...@@ -117,7 +117,7 @@ following these rules:
created (requires GitLab Runner v1.1.0 or higher) created (requires GitLab Runner v1.1.0 or higher)
To override the default behavior, you can To override the default behavior, you can
[specify a service alias](#available-settings-for-services-entry). [specify a service alias](#available-settings-for-services).
## Define `image` and `services` from `.gitlab-ci.yml` ## Define `image` and `services` from `.gitlab-ci.yml`
...@@ -183,8 +183,7 @@ test: ...@@ -183,8 +183,7 @@ test:
## Extended Docker configuration options ## Extended Docker configuration options
> **Note:** > Introduced in GitLab and GitLab Runner 9.4.
This feature requires GitLab 9.4 and GitLab Runner 9.4 or higher.
When configuring the `image` or `services` entries, you can use a string or a map as When configuring the `image` or `services` entries, you can use a string or a map as
options: options:
...@@ -221,28 +220,29 @@ For example, the following two definitions are equal: ...@@ -221,28 +220,29 @@ For example, the following two definitions are equal:
### Available settings for `image` ### Available settings for `image`
> **Note:** > Introduced in GitLab and GitLab Runner 9.4.
This feature requires GitLab 9.4 and GitLab Runner 9.4 or higher.
| Setting | Required | Description | | Setting | Required | GitLab version | Description |
|------------|----------|-------------| |------------|----------|----------------| ----------- |
| `name` | yes, when used with any other option | Full name of the image that should be used. It should contain the Registry part if needed. | | `name` | yes, when used with any other option | 9.4 |Full name of the image that should be used. It should contain the Registry part if needed. |
| `entrypoint` | no | Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. | | `entrypoint` | no | 9.4 |Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. |
### Available settings for `services` ### Available settings for `services`
> **Note:** > Introduced in GitLab and GitLab Runner 9.4.
This feature requires GitLab 9.4 and GitLab Runner 9.4 or higher.
| Setting | Required | Description | | Setting | Required | GitLab version | Description |
|------------|----------|-------------| |------------|----------|----------------| ----------- |
| `name` | yes, when used with any other option | Full name of the image that should be used. It should contain the Registry part if needed. | | `name` | yes, when used with any other option | 9.4 | Full name of the image that should be used. It should contain the Registry part if needed. |
| `entrypoint` | no | Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. | | `entrypoint` | no | 9.4 |Command or script that should be executed as the container's entrypoint. It will be translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`][entrypoint] directive, where each shell token is a separate string in the array. |
| `command` | no | Command or script that should be used as the container's command. It will be translated to arguments passed to Docker after the image's name. The syntax is similar to [`Dockerfile`'s `CMD`][cmd] directive, where each shell token is a separate string in the array. | | `command` | no | 9.4 |Command or script that should be used as the container's command. It will be translated to arguments passed to Docker after the image's name. The syntax is similar to [`Dockerfile`'s `CMD`][cmd] directive, where each shell token is a separate string in the array. |
| `alias` | no | Additional alias that can be used to access the service from the job's container. Read [Accessing the services](#accessing-the-services) for more information. | | `alias` | no | 9.4 |Additional alias that can be used to access the service from the job's container. Read [Accessing the services](#accessing-the-services) for more information. |
### Starting multiple services from the same image ### Starting multiple services from the same image
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Before the new extended Docker configuration options, the following configuration Before the new extended Docker configuration options, the following configuration
would not work properly: would not work properly:
...@@ -274,6 +274,9 @@ in `.gitlab-ci.yml` file. ...@@ -274,6 +274,9 @@ in `.gitlab-ci.yml` file.
### Setting a command for the service ### Setting a command for the service
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Let's assume you have a `super/sql:latest` image with some SQL database Let's assume you have a `super/sql:latest` image with some SQL database
inside it and you would like to use it as a service for your job. Let's also inside it and you would like to use it as a service for your job. Let's also
assume that this image doesn't start the database process while starting assume that this image doesn't start the database process while starting
...@@ -313,6 +316,9 @@ As you can see, the syntax of `command` is similar to [Dockerfile's `CMD`][cmd]. ...@@ -313,6 +316,9 @@ As you can see, the syntax of `command` is similar to [Dockerfile's `CMD`][cmd].
### Overriding the entrypoint of an image ### Overriding the entrypoint of an image
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
Let's assume you have a `super/sql:experimental` image with some SQL database Let's assume you have a `super/sql:experimental` image with some SQL database
inside it and you would like to use it as a base image for your job because you inside it and you would like to use it as a base image for your job because you
want to execute some tests with this database binary. Let's also assume that want to execute some tests with this database binary. Let's also assume that
......
...@@ -29,5 +29,12 @@ ...@@ -29,5 +29,12 @@
1. Click **Create project**. 1. Click **Create project**.
## From a template
To kickstart your development GitLab projects can be started from a template.
For example, one of the templates included is Ruby on Rails. When filling out the
form for new projects, click the 'Ruby on Rails' button. During project creation,
this will import a Ruby on Rails template with GitLab CI preconfigured.
[import it]: ../workflow/importing/README.md [import it]: ../workflow/importing/README.md
[reserved]: ../user/reserved_names.md [reserved]: ../user/reserved_names.md
...@@ -22,6 +22,7 @@ We've gathered some resources to help you to get the best from Git with GitLab. ...@@ -22,6 +22,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit) - [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
- [Squashing commits](../../workflow/gitlab_flow.md#squashing-commits-with-rebase) - [Squashing commits](../../workflow/gitlab_flow.md#squashing-commits-with-rebase)
- **Articles:** - **Articles:**
- [Numerous _undo_ possibilities in Git](../../articles/numerous_undo_possibilities_in_git/index.md)
- [How to install Git](../../articles/how_to_install_git/index.md) - [How to install Git](../../articles/how_to_install_git/index.md)
- [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/) - [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/)
- [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/) - [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/)
......
# Import your project from Bitbucket to GitLab
Import your projects from Bitbucket to GitLab with minimal effort.
## Overview
>**Note:**
The [Bitbucket integration][bb-import] must be first enabled in order to be
able to import your projects from Bitbucket. Ask your GitLab administrator
to enable this if not already.
- At its current state, the Bitbucket importer can import:
- the repository description (GitLab 7.7+)
- the Git repository data (GitLab 7.7+)
- the issues (GitLab 7.7+)
- the issue comments (GitLab 8.15+)
- the pull requests (GitLab 8.4+)
- the pull request comments (GitLab 8.15+)
- the milestones (GitLab 8.15+)
- the wiki (GitLab 8.15+)
- References to pull requests and issues are preserved (GitLab 8.7+)
- Repository public access is retained. If a repository is private in Bitbucket
it will be created as private in GitLab as well.
## How it works
When issues/pull requests are being imported, the Bitbucket importer tries to find
the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this
to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
and **associated their Bitbucket account**. If the user is not
found in GitLab's database, the project creator (most of the times the current
user that started the import process) is set as the author, but a reference on
the issue about the original Bitbucket author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
## Importing your Bitbucket repositories
1. Sign in to GitLab and go to your dashboard.
1. Click on **New project**.
![New project in GitLab](img/bitbucket_import_new_project.png)
1. Click on the "Bitbucket" button
![Bitbucket](img/import_projects_from_new_project_page.png)
1. Grant GitLab access to your Bitbucket account
![Grant access](img/bitbucket_import_grant_access.png)
1. Click on the projects that you'd like to import or **Import all projects**.
You can also select the namespace under which each project will be
imported.
![Import projects](img/bitbucket_import_select_project.png)
[bb-import]: ../../../integration/bitbucket.md
[social sign-in]: ../../profile/account/social_sign_in.md
# Migrating from ClearCase
[ClearCase](https://www-03.ibm.com/software/products/en/clearcase/) is a set of
tools developed by IBM which also include a centralized version control system
similar to Git.
A good read of ClearCase's basic concepts is can be found in this [StackOverflow
post](https://stackoverflow.com/a/645771/974710).
The following table illustrates the main differences between ClearCase and Git:
| Aspect | ClearCase | Git |
| ------ | --------- | --- |
| Repository model | Client-server | Distributed |
| Revision IDs | Branch + number | Global alphanumeric ID |
| Scope of Change | File | Directory tree snapshot |
| Concurrency model | Merge | Merge |
| Storage Method | Deltas | Full content |
| Client | CLI, Eclipse, CC Client | CLI, Eclipse, Git client/GUIs |
| Server | UNIX, Windows legacy systems | UNIX, macOS |
| License | Proprietary | GPL |
_Taken from the slides [ClearCase and the journey to Git](https://www.open.collab.net/media/pdfs/ClearCase-and-the-journey-to-Git.pdf) provided by collab.net_
## Why migrate
ClearCase can be difficult to manage both from a user and an admin perspective.
Migrating to Git/GitLab there is:
- **No licensing costs**, Git is GPL while ClearCase is proprietary.
- **Shorter learning curve**, Git has a big community and a vast number of
tutorials to get you started.
- **Integration with modern tools**, migrating to Git and GitLab you can have
an open source end-to-end software development platform with built-in version
control, issue tracking, code review, CI/CD, and more.
## How to migrate
While there doesn't exist a tool to fully migrate from ClearCase to Git, here
are some useful links to get you started:
- [Bridge for Git and ClearCase](https://github.com/charleso/git-cc)
- [Slides "ClearCase and the journey to Git"](https://www.open.collab.net/media/pdfs/ClearCase-and-the-journey-to-Git.pdf)
- [ClearCase to Git](https://therub.org/2013/07/19/clearcase-to-git/)
- [Dual syncing ClearCase to Git](https://therub.org/2013/10/22/dual-syncing-clearcase-and-git/)
- [Moving to Git from ClearCase](https://sateeshkumarb.wordpress.com/2011/01/15/moving-to-git-from-clearcase/)
- [ClearCase to Git webinar](https://www.brighttalk.com/webcast/11817/162473/clearcase-to-git)
# Import your project from FogBugz to GitLab
It only takes a few simple steps to import your project from FogBugz.
The importer will import all of your cases and comments with original case
numbers and timestamps. You will also have the opportunity to map FogBugz
users to GitLab users.
1. From your GitLab dashboard click 'New project'
1. Click on the 'FogBugz' button
![FogBugz](img/fogbugz_import_select_fogbogz.png)
1. Enter your FogBugz URL, email address, and password.
![Login](img/fogbugz_import_login.png)
1. Create mapping from FogBugz users to GitLab users.
![User Map](img/fogbugz_import_user_map.png)
1. Select the projects you wish to import by clicking the Import buttons
![Import Project](img/fogbugz_import_select_project.png)
1. Once the import has finished click the link to take you to the project
dashboard. Follow the directions to push your existing repository.
![Finished](img/fogbugz_import_finished.png)
# Import your project from Gitea to GitLab
Import your projects from Gitea to GitLab with minimal effort.
## Overview
>**Note:**
This requires Gitea `v1.0.0` or newer.
- At its current state, Gitea importer can import:
- the repository description (GitLab 8.15+)
- the Git repository data (GitLab 8.15+)
- the issues (GitLab 8.15+)
- the pull requests (GitLab 8.15+)
- the milestones (GitLab 8.15+)
- the labels (GitLab 8.15+)
- Repository public access is retained. If a repository is private in Gitea
it will be created as private in GitLab as well.
## How it works
Since Gitea is currently not an OAuth provider, author/assignee cannot be mapped
to users in your GitLab's instance. This means that the project creator (most of
the times the current user that started the import process) is set as the author,
but a reference on the issue about the original Gitea author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
## Importing your Gitea repositories
The importer page is visible when you create a new project.
![New project page on GitLab](img/import_projects_from_new_project_page.png)
Click on the **Gitea** link and the import authorization process will start.
![New Gitea project import](img/import_projects_from_gitea_new_import.png)
### Authorize access to your repositories using a personal access token
With this method, you will perform a one-off authorization with Gitea to grant
GitLab access your repositories:
1. Go to <https://you-gitea-instance/user/settings/applications> (replace
`you-gitea-instance` with the host of your Gitea instance).
1. Click **Generate New Token**.
1. Enter a token description.
1. Click **Generate Token**.
1. Copy the token hash.
1. Go back to GitLab and provide the token to the Gitea importer.
1. Hit the **List Your Gitea Repositories** button and wait while GitLab reads
your repositories' information. Once done, you'll be taken to the importer
page to select the repositories to import.
### Select which repositories to import
After you've authorized access to your Gitea repositories, you will be
redirected to the Gitea importer page.
From there, you can see the import statuses of your Gitea repositories.
- Those that are being imported will show a _started_ status,
- those already successfully imported will be green with a _done_ status,
- whereas those that are not yet imported will have an **Import** button on the
right side of the table.
If you want, you can import all your Gitea projects in one go by hitting
**Import all projects** in the upper left corner.
![Gitea importer page](img/import_projects_from_github_importer.png)
---
You can also choose a different name for the project and a different namespace,
if you have the privileges to do so.
# Import your project from GitHub to GitLab
Import your projects from GitHub to GitLab with minimal effort.
## Overview
>**Note:**
If you are an administrator you can enable the [GitHub integration][gh-import]
in your GitLab instance sitewide. This configuration is optional, users will
still be able to import their GitHub repositories with a
[personal access token][gh-token].
>**Note:**
Administrators of a GitLab instance (Community or Enterprise Edition) can also
use the [GitHub rake task][gh-rake] to import projects from GitHub without the
constrains of a Sidekiq worker.
- At its current state, GitHub importer can import:
- the repository description (GitLab 7.7+)
- the Git repository data (GitLab 7.7+)
- the issues (GitLab 7.7+)
- the pull requests (GitLab 8.4+)
- the wiki pages (GitLab 8.4+)
- the milestones (GitLab 8.7+)
- the labels (GitLab 8.7+)
- the release note descriptions (GitLab 8.12+)
- References to pull requests and issues are preserved (GitLab 8.7+)
- Repository public access is retained. If a repository is private in GitHub
it will be created as private in GitLab as well.
## How it works
When issues/pull requests are being imported, the GitHub importer tries to find
the GitHub author/assignee in GitLab's database using the GitHub ID. For this
to work, the GitHub author/assignee should have signed in beforehand in GitLab
and **associated their GitHub account**. If the user is not
found in GitLab's database, the project creator (most of the times the current
user that started the import process) is set as the author, but a reference on
the issue about the original GitHub author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
## Importing your GitHub repositories
The importer page is visible when you create a new project.
![New project page on GitLab](img/import_projects_from_new_project_page.png)
Click on the **GitHub** link and the import authorization process will start.
There are two ways to authorize access to your GitHub repositories:
1. [Using the GitHub integration][gh-integration] (if it's enabled by your
GitLab administrator). This is the preferred way as it's possible to
preserve the GitHub authors/assignees. Read more in the [How it works](#how-it-works)
section.
1. [Using a personal access token][gh-token] provided by GitHub.
![Select authentication method](img/import_projects_from_github_select_auth_method.png)
### Authorize access to your repositories using the GitHub integration
If the [GitHub integration][gh-import] is enabled by your GitLab administrator,
you can use it instead of the personal access token.
1. First you may want to connect your GitHub account to GitLab in order for
the username mapping to be correct.
1. Once you connect GitHub, click the **List your GitHub repositories** button
and you will be redirected to GitHub for permission to access your projects.
1. After accepting, you'll be automatically redirected to the importer.
You can now go on and [select which repositories to import](#select-which-repositories-to-import).
### Authorize access to your repositories using a personal access token
>**Note:**
For a proper author/assignee mapping for issues and pull requests, the
[GitHub integration][gh-integration] should be used instead of the
[personal access token][gh-token]. If the GitHub integration is enabled by your
GitLab administrator, it should be the preferred method to import your repositories.
Read more in the [How it works](#how-it-works) section.
If you are not using the GitHub integration, you can still perform a one-off
authorization with GitHub to grant GitLab access your repositories:
1. Go to <https://github.com/settings/tokens/new>.
1. Enter a token description.
1. Check the `repo` scope.
1. Click **Generate token**.
1. Copy the token hash.
1. Go back to GitLab and provide the token to the GitHub importer.
1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads
your repositories' information. Once done, you'll be taken to the importer
page to select the repositories to import.
### Select which repositories to import
After you've authorized access to your GitHub repositories, you will be
redirected to the GitHub importer page.
From there, you can see the import statuses of your GitHub repositories.
- Those that are being imported will show a _started_ status,
- those already successfully imported will be green with a _done_ status,
- whereas those that are not yet imported will have an **Import** button on the
right side of the table.
If you want, you can import all your GitHub projects in one go by hitting
**Import all projects** in the upper left corner.
![GitHub importer page](img/import_projects_from_github_importer.png)
---
You can also choose a different name for the project and a different namespace,
if you have the privileges to do so.
[gh-import]: ../../../integration/github.md "GitHub integration"
[gh-rake]: ../../../administration/raketasks/github_import.md "GitHub rake task"
[gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration
[gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token
# Project importing from GitLab.com to your private GitLab instance
You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if
GitLab support is enabled on your GitLab instance.
You can read more about GitLab support [here](http://docs.gitlab.com/ce/integration/gitlab.html)
To get to the importer page you need to go to "New project" page.
>**Note:**
If you are interested in importing Wiki and Merge Request data to your new
instance, you'll need to follow the instructions for [project export](../settings/import_export.md)
![New project page](img/gitlab_new_project_page.png)
Click on the "Import projects from GitLab.com" link and you will be redirected to GitLab.com
for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
![Importer page](img/gitlab_importer.png)
To import a project, you can simple click "Import". The importer will import your repository and issues.
Once the importer is done, a new GitLab project will be created with your imported data.
# Migrating projects to a GitLab instance
1. [From Bitbucket.org](bitbucket.md)
1. [From GitHub.com of GitHub Enterprise](github.md)
1. [From GitLab.com](gitlab_com.md)
1. [From FogBugz](fogbugz.md)
1. [From Gitea](gitea.md)
1. [From SVN](svn.md)
1. [From ClearCase](clearcase.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
repository is too large the import can timeout.
## Migrating from self-hosted GitLab 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.
If you want to retain all metadata like issues and merge requests, you can use
the [import/export feature](../settings/import_export.md).
# Migrating from SVN to GitLab
Subversion (SVN) is a central version control system (VCS) while
Git is a distributed version control system. There are some major differences
between the two, for more information consult your favorite search engine.
## Overview
There are two approaches to SVN to Git migration:
1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
- Makes the GitLab repository to mirror the SVN project.
- Git and SVN repositories are kept in sync; you can use either one.
- Smoothens the migration process and allows to manage migration risks.
1. [Cut over migration](#cut-over-migration-with-svn2git) which:
- Translates and imports the existing data and history from SVN to Git.
- Is a fire and forget approach, good for smaller teams.
## Smooth migration with a Git/SVN mirror using SubGit
[SubGit](https://subgit.com) is a tool for a smooth, stress-free SVN to Git
migration. It creates a writable Git mirror of a local or remote Subversion
repository and that way you can use both Subversion and Git as long as you like.
It requires access to your GitLab server as it talks with the Git repositories
directly in a filesystem level.
### SubGit prerequisites
1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
1. Download SubGit from https://subgit.com/download/.
1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
command will be available at `/opt/subgit-VERSION/bin/subgit`.
### SubGit configuration
The first step to mirror you SVN repository in GitLab is to create a new empty
project which will be used as a mirror. For Omnibus installations the path to
the repository will be located at
`/var/opt/gitlab/git-data/repositories/USER/REPO.git` by default. For
installations from source, the default repository directory will be
`/home/git/repositories/USER/REPO.git`. For convenience, assign this path to a
variable:
```
GIT_REPO_PATH=/var/opt/gitlab/git-data/repositories/USER/REPOS.git
```
SubGit will keep this repository in sync with a remote SVN project. For
convenience, assign your remote SVN project URL to a variable:
```
SVN_PROJECT_URL=http://svn.company.com/repos/project
```
Next you need to run SubGit to set up a Git/SVN mirror. Make sure the following
`subgit` command is ran on behalf of the same user that keeps ownership of
GitLab Git repositories (by default `git`):
```
subgit configure --layout auto $SVN_PROJECT_URL $GIT_REPO_PATH
```
Adjust authors and branches mappings, if necessary. Open with your favorite
text editor:
```
edit $GIT_REPO_PATH/subgit/authors.txt
edit $GIT_REPO_PATH/subgit/config
```
For more information regarding the SubGit configuration options, refer to
[SubGit's documentation](https://subgit.com/documentation.html) website.
### Initial translation
Now that SubGit has configured the Git/SVN repos, run `subgit` to perform the
initial translation of existing SVN revisions into the Git repository:
```
subgit install $GIT_REPO_PATH
```
After the initial translation is completed, the Git repository and the SVN
project will be kept in sync by `subgit` - new Git commits will be translated to
SVN revisions and new SVN revisions will be translated to Git commits. Mirror
works transparently and does not require any special commands.
If you would prefer to perform one-time cut over migration with `subgit`, use
the `import` command instead of `install`:
```
subgit import $GIT_REPO_PATH
```
### SubGit licensing
Running SubGit in a mirror mode requires a
[registration](https://subgit.com/pricing.html). Registration is free for open
source, academic and startup projects.
We're currently working on deeper GitLab/SubGit integration. You may track our
progress at [this issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/990).
### SubGit support
For any questions related to SVN to GitLab migration with SubGit, you can
contact the SubGit team directly at [support@subgit.com](mailto:support@subgit.com).
## Cut over migration with svn2git
If you are currently using an SVN repository, you can migrate the repository
to Git and GitLab. We recommend a hard cut over - run the migration command once
and then have all developers start using the new GitLab repository immediately.
Otherwise, it's hard to keep changing in sync in both directions. The conversion
process should be run on a local workstation.
Install `svn2git`. On all systems you can install as a Ruby gem if you already
have Ruby and Git installed.
```bash
sudo gem install svn2git
```
On Debian-based Linux distributions you can install the native packages:
```bash
sudo apt-get install git-core git-svn ruby
```
Optionally, prepare an authors file so `svn2git` can map SVN authors to Git authors.
If you choose not to create the authors file then commits will not be attributed
to the correct GitLab user. Some users may not consider this a big issue while
others will want to ensure they complete this step. If you choose to map authors
you will be required to map every author that is present on changes in the SVN
repository. If you don't, the conversion will fail and you will have to update
the author file accordingly. The following command will search through the
repository and output a list of authors.
```bash
svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
```
Use the output from the last command to construct the authors file.
Create a file called `authors.txt` and add one mapping per line.
```
janedoe = Jane Doe <janedoe@example.com>
johndoe = John Doe <johndoe@example.com>
```
If your SVN repository is in the standard format (trunk, branches, tags,
not nested) the conversion is simple. For a non-standard repository see
[svn2git documentation](https://github.com/nirvdrum/svn2git). The following
command will checkout the repository and do the conversion in the current
working directory. Be sure to create a new directory for each repository before
running the `svn2git` command. The conversion process will take some time.
```bash
svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
```
If your SVN repository requires a username and password add the
`--username <username>` and `--password <password` flags to the above command.
`svn2git` also supports excluding certain file paths, branches, tags, etc. See
[svn2git documentation](https://github.com/nirvdrum/svn2git) or run
`svn2git --help` for full documentation on all of the available options.
Create a new GitLab project, where you will eventually push your converted code.
Copy the SSH or HTTP(S) repository URL from the project page. Add the GitLab
repository as a Git remote and push all the changes. This will push all commits,
branches and tags.
```bash
git remote add origin git@gitlab.com:<group>/<project>.git
git push --all origin
git push --tags origin
```
## Contribute to this guide
We welcome all contributions that would expand this guide with instructions on
how to migrate from SVN and other version control systems.
...@@ -90,11 +90,11 @@ from your fork to the upstream project ...@@ -90,11 +90,11 @@ from your fork to the upstream project
## Import or export a project ## Import or export a project
- Import a project from: - [Import a project](import/index.md) from:
- [GitHub to GitLab](../../workflow/importing/import_projects_from_github.md) - [GitHub to GitLab](import/github.md)
- [BitBucket to GitLab](../../workflow/importing/import_projects_from_bitbucket.md) - [BitBucket to GitLab](import/bitbucket.md)
- [Gitea to GitLab](../../workflow/importing/import_projects_from_gitea.md) - [Gitea to GitLab](import/gitea.md)
- [FogBugz to GitLab](../../workflow/importing/import_projects_from_fogbugz.md) - [FogBugz to GitLab](import/fogbugz.md)
- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data) - [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data)
- [Importing and exporting projects between GitLab instances](settings/import_export.md) - [Importing and exporting projects between GitLab instances](settings/import_export.md)
......
# Migrating projects to a GitLab instance This document was moved to a [new location](../../user/project/import/index.md).
1. [Bitbucket](import_projects_from_bitbucket.md)
1. [GitHub](import_projects_from_github.md)
1. [GitLab.com](import_projects_from_gitlab_com.md)
1. [FogBugz](import_projects_from_fogbugz.md)
1. [Gitea](import_projects_from_gitea.md)
1. [SVN](migrating_from_svn.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
repository is too large the import can timeout.
### Migrating from self-hosted GitLab 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.
# Import your project from Bitbucket to GitLab This document was moved to a [new location](../../user/project/import/bitbucket.md).
Import your projects from Bitbucket to GitLab with minimal effort.
## Overview
>**Note:**
The [Bitbucket integration][bb-import] must be first enabled in order to be
able to import your projects from Bitbucket. Ask your GitLab administrator
to enable this if not already.
- At its current state, the Bitbucket importer can import:
- the repository description (GitLab 7.7+)
- the Git repository data (GitLab 7.7+)
- the issues (GitLab 7.7+)
- the issue comments (GitLab 8.15+)
- the pull requests (GitLab 8.4+)
- the pull request comments (GitLab 8.15+)
- the milestones (GitLab 8.15+)
- the wiki (GitLab 8.15+)
- References to pull requests and issues are preserved (GitLab 8.7+)
- Repository public access is retained. If a repository is private in Bitbucket
it will be created as private in GitLab as well.
## How it works
When issues/pull requests are being imported, the Bitbucket importer tries to find
the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this
to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
and **associated their Bitbucket account**. If the user is not
found in GitLab's database, the project creator (most of the times the current
user that started the import process) is set as the author, but a reference on
the issue about the original Bitbucket author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
## Importing your Bitbucket repositories
1. Sign in to GitLab and go to your dashboard.
1. Click on **New project**.
![New project in GitLab](img/bitbucket_import_new_project.png)
1. Click on the "Bitbucket" button
![Bitbucket](img/import_projects_from_new_project_page.png)
1. Grant GitLab access to your Bitbucket account
![Grant access](img/bitbucket_import_grant_access.png)
1. Click on the projects that you'd like to import or **Import all projects**.
You can also select the namespace under which each project will be
imported.
![Import projects](img/bitbucket_import_select_project.png)
[bb-import]: ../../integration/bitbucket.md
[social sign-in]: ../../user/profile/account/social_sign_in.md
# Import your project from FogBugz to GitLab This document was moved to a [new location](../../user/project/import/fogbugz.md).
It only takes a few simple steps to import your project from FogBugz.
The importer will import all of your cases and comments with original case
numbers and timestamps. You will also have the opportunity to map FogBugz
users to GitLab users.
* From your GitLab dashboard click 'New project'
* Click on the 'FogBugz' button
![FogBugz](fogbugz_importer/fogbugz_import_select_fogbogz.png)
* Enter your FogBugz URL, email address, and password.
![Login](fogbugz_importer/fogbugz_import_login.png)
* Create mapping from FogBugz users to GitLab users.
![User Map](fogbugz_importer/fogbugz_import_user_map.png)
* Select the projects you wish to import by clicking the Import buttons
![Import Project](fogbugz_importer/fogbugz_import_select_project.png)
* Once the import has finished click the link to take you to the project
dashboard. Follow the directions to push your existing repository.
![Finished](fogbugz_importer/fogbugz_import_finished.png)
# Import your project from Gitea to GitLab This document was moved to a [new location](../../user/project/import/gitea.md).
Import your projects from Gitea to GitLab with minimal effort.
## Overview
>**Note:**
This requires Gitea `v1.0.0` or newer.
- At its current state, Gitea importer can import:
- the repository description (GitLab 8.15+)
- the Git repository data (GitLab 8.15+)
- the issues (GitLab 8.15+)
- the pull requests (GitLab 8.15+)
- the milestones (GitLab 8.15+)
- the labels (GitLab 8.15+)
- Repository public access is retained. If a repository is private in Gitea
it will be created as private in GitLab as well.
## How it works
Since Gitea is currently not an OAuth provider, author/assignee cannot be mapped
to users in your GitLab's instance. This means that the project creator (most of
the times the current user that started the import process) is set as the author,
but a reference on the issue about the original Gitea author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
## Importing your Gitea repositories
The importer page is visible when you create a new project.
![New project page on GitLab](img/import_projects_from_new_project_page.png)
Click on the **Gitea** link and the import authorization process will start.
![New Gitea project import](img/import_projects_from_gitea_new_import.png)
### Authorize access to your repositories using a personal access token
With this method, you will perform a one-off authorization with Gitea to grant
GitLab access your repositories:
1. Go to <https://you-gitea-instance/user/settings/applications> (replace
`you-gitea-instance` with the host of your Gitea instance).
1. Click **Generate New Token**.
1. Enter a token description.
1. Click **Generate Token**.
1. Copy the token hash.
1. Go back to GitLab and provide the token to the Gitea importer.
1. Hit the **List Your Gitea Repositories** button and wait while GitLab reads
your repositories' information. Once done, you'll be taken to the importer
page to select the repositories to import.
### Select which repositories to import
After you've authorized access to your Gitea repositories, you will be
redirected to the Gitea importer page.
From there, you can see the import statuses of your Gitea repositories.
- Those that are being imported will show a _started_ status,
- those already successfully imported will be green with a _done_ status,
- whereas those that are not yet imported will have an **Import** button on the
right side of the table.
If you want, you can import all your Gitea projects in one go by hitting
**Import all projects** in the upper left corner.
![Gitea importer page](img/import_projects_from_github_importer.png)
---
You can also choose a different name for the project and a different namespace,
if you have the privileges to do so.
# Import your project from GitHub to GitLab This document was moved to a [new location](../../user/project/import/github.md).
Import your projects from GitHub to GitLab with minimal effort.
## Overview
>**Note:**
If you are an administrator you can enable the [GitHub integration][gh-import]
in your GitLab instance sitewide. This configuration is optional, users will
still be able to import their GitHub repositories with a
[personal access token][gh-token].
>**Note:**
Administrators of a GitLab instance (Community or Enterprise Edition) can also
use the [GitHub rake task][gh-rake] to import projects from GitHub without the
constrains of a Sidekiq worker.
- At its current state, GitHub importer can import:
- the repository description (GitLab 7.7+)
- the Git repository data (GitLab 7.7+)
- the issues (GitLab 7.7+)
- the pull requests (GitLab 8.4+)
- the wiki pages (GitLab 8.4+)
- the milestones (GitLab 8.7+)
- the labels (GitLab 8.7+)
- the release note descriptions (GitLab 8.12+)
- References to pull requests and issues are preserved (GitLab 8.7+)
- Repository public access is retained. If a repository is private in GitHub
it will be created as private in GitLab as well.
## How it works
When issues/pull requests are being imported, the GitHub importer tries to find
the GitHub author/assignee in GitLab's database using the GitHub ID. For this
to work, the GitHub author/assignee should have signed in beforehand in GitLab
and **associated their GitHub account**. If the user is not
found in GitLab's database, the project creator (most of the times the current
user that started the import process) is set as the author, but a reference on
the issue about the original GitHub author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
## Importing your GitHub repositories
The importer page is visible when you create a new project.
![New project page on GitLab](img/import_projects_from_new_project_page.png)
Click on the **GitHub** link and the import authorization process will start.
There are two ways to authorize access to your GitHub repositories:
1. [Using the GitHub integration][gh-integration] (if it's enabled by your
GitLab administrator). This is the preferred way as it's possible to
preserve the GitHub authors/assignees. Read more in the [How it works](#how-it-works)
section.
1. [Using a personal access token][gh-token] provided by GitHub.
![Select authentication method](img/import_projects_from_github_select_auth_method.png)
### Authorize access to your repositories using the GitHub integration
If the [GitHub integration][gh-import] is enabled by your GitLab administrator,
you can use it instead of the personal access token.
1. First you may want to connect your GitHub account to GitLab in order for
the username mapping to be correct.
1. Once you connect GitHub, click the **List your GitHub repositories** button
and you will be redirected to GitHub for permission to access your projects.
1. After accepting, you'll be automatically redirected to the importer.
You can now go on and [select which repositories to import](#select-which-repositories-to-import).
### Authorize access to your repositories using a personal access token
>**Note:**
For a proper author/assignee mapping for issues and pull requests, the
[GitHub integration][gh-integration] should be used instead of the
[personal access token][gh-token]. If the GitHub integration is enabled by your
GitLab administrator, it should be the preferred method to import your repositories.
Read more in the [How it works](#how-it-works) section.
If you are not using the GitHub integration, you can still perform a one-off
authorization with GitHub to grant GitLab access your repositories:
1. Go to <https://github.com/settings/tokens/new>.
1. Enter a token description.
1. Check the `repo` scope.
1. Click **Generate token**.
1. Copy the token hash.
1. Go back to GitLab and provide the token to the GitHub importer.
1. Hit the **List Your GitHub Repositories** button and wait while GitLab reads
your repositories' information. Once done, you'll be taken to the importer
page to select the repositories to import.
### Select which repositories to import
After you've authorized access to your GitHub repositories, you will be
redirected to the GitHub importer page.
From there, you can see the import statuses of your GitHub repositories.
- Those that are being imported will show a _started_ status,
- those already successfully imported will be green with a _done_ status,
- whereas those that are not yet imported will have an **Import** button on the
right side of the table.
If you want, you can import all your GitHub projects in one go by hitting
**Import all projects** in the upper left corner.
![GitHub importer page](img/import_projects_from_github_importer.png)
---
You can also choose a different name for the project and a different namespace,
if you have the privileges to do so.
[gh-import]: ../../integration/github.md "GitHub integration"
[gh-rake]: ../../administration/raketasks/github_import.md "GitHub rake task"
[gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration
[gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token
# Project importing from GitLab.com to your private GitLab instance This document was moved to a [new location](../../user/project/import/gitlab_com.md).
You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if
GitLab support is enabled on your GitLab instance.
You can read more about GitLab support [here](http://docs.gitlab.com/ce/integration/gitlab.html)
To get to the importer page you need to go to "New project" page.
>**Note:**
If you are interested in importing Wiki and Merge Request data to your new instance, you'll need to follow the instructions for [project export](../../user/project/settings/import_export.md)
![New project page](gitlab_importer/new_project_page.png)
Click on the "Import projects from GitLab.com" link and you will be redirected to GitLab.com
for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
![Importer page](gitlab_importer/importer.png)
To import a project, you can simple click "Import". The importer will import your repository and issues.
Once the importer is done, a new GitLab project will be created with your imported data.
\ No newline at end of file
# Migrating from SVN to GitLab This document was moved to a [new location](../../user/project/import/svn.md).
Subversion (SVN) is a central version control system (VCS) while
Git is a distributed version control system. There are some major differences
between the two, for more information consult your favorite search engine.
## Overview
There are two approaches to SVN to Git migration:
1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
- Makes the GitLab repository to mirror the SVN project.
- Git and SVN repositories are kept in sync; you can use either one.
- Smoothens the migration process and allows to manage migration risks.
1. [Cut over migration](#cut-over-migration-with-svn2git) which:
- Translates and imports the existing data and history from SVN to Git.
- Is a fire and forget approach, good for smaller teams.
## Smooth migration with a Git/SVN mirror using SubGit
[SubGit](https://subgit.com) is a tool for a smooth, stress-free SVN to Git
migration. It creates a writable Git mirror of a local or remote Subversion
repository and that way you can use both Subversion and Git as long as you like.
It requires access to your GitLab server as it talks with the Git repositories
directly in a filesystem level.
### SubGit prerequisites
1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
1. Download SubGit from https://subgit.com/download/.
1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
command will be available at `/opt/subgit-VERSION/bin/subgit`.
### SubGit configuration
The first step to mirror you SVN repository in GitLab is to create a new empty
project which will be used as a mirror. For Omnibus installations the path to
the repository will be located at
`/var/opt/gitlab/git-data/repositories/USER/REPO.git` by default. For
installations from source, the default repository directory will be
`/home/git/repositories/USER/REPO.git`. For convenience, assign this path to a
variable:
```
GIT_REPO_PATH=/var/opt/gitlab/git-data/repositories/USER/REPOS.git
```
SubGit will keep this repository in sync with a remote SVN project. For
convenience, assign your remote SVN project URL to a variable:
```
SVN_PROJECT_URL=http://svn.company.com/repos/project
```
Next you need to run SubGit to set up a Git/SVN mirror. Make sure the following
`subgit` command is ran on behalf of the same user that keeps ownership of
GitLab Git repositories (by default `git`):
```
subgit configure --layout auto $SVN_PROJECT_URL $GIT_REPO_PATH
```
Adjust authors and branches mappings, if necessary. Open with your favorite
text editor:
```
edit $GIT_REPO_PATH/subgit/authors.txt
edit $GIT_REPO_PATH/subgit/config
```
For more information regarding the SubGit configuration options, refer to
[SubGit's documentation](https://subgit.com/documentation.html) website.
### Initial translation
Now that SubGit has configured the Git/SVN repos, run `subgit` to perform the
initial translation of existing SVN revisions into the Git repository:
```
subgit install $GIT_REPO_PATH
```
After the initial translation is completed, the Git repository and the SVN
project will be kept in sync by `subgit` - new Git commits will be translated to
SVN revisions and new SVN revisions will be translated to Git commits. Mirror
works transparently and does not require any special commands.
If you would prefer to perform one-time cut over migration with `subgit`, use
the `import` command instead of `install`:
```
subgit import $GIT_REPO_PATH
```
### SubGit licensing
Running SubGit in a mirror mode requires a
[registration](https://subgit.com/pricing.html). Registration is free for open
source, academic and startup projects.
We're currently working on deeper GitLab/SubGit integration. You may track our
progress at [this issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/990).
### SubGit support
For any questions related to SVN to GitLab migration with SubGit, you can
contact the SubGit team directly at [support@subgit.com](mailto:support@subgit.com).
## Cut over migration with svn2git
If you are currently using an SVN repository, you can migrate the repository
to Git and GitLab. We recommend a hard cut over - run the migration command once
and then have all developers start using the new GitLab repository immediately.
Otherwise, it's hard to keep changing in sync in both directions. The conversion
process should be run on a local workstation.
Install `svn2git`. On all systems you can install as a Ruby gem if you already
have Ruby and Git installed.
```bash
sudo gem install svn2git
```
On Debian-based Linux distributions you can install the native packages:
```bash
sudo apt-get install git-core git-svn ruby
```
Optionally, prepare an authors file so `svn2git` can map SVN authors to Git authors.
If you choose not to create the authors file then commits will not be attributed
to the correct GitLab user. Some users may not consider this a big issue while
others will want to ensure they complete this step. If you choose to map authors
you will be required to map every author that is present on changes in the SVN
repository. If you don't, the conversion will fail and you will have to update
the author file accordingly. The following command will search through the
repository and output a list of authors.
```bash
svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
```
Use the output from the last command to construct the authors file.
Create a file called `authors.txt` and add one mapping per line.
```
janedoe = Jane Doe <janedoe@example.com>
johndoe = John Doe <johndoe@example.com>
```
If your SVN repository is in the standard format (trunk, branches, tags,
not nested) the conversion is simple. For a non-standard repository see
[svn2git documentation](https://github.com/nirvdrum/svn2git). The following
command will checkout the repository and do the conversion in the current
working directory. Be sure to create a new directory for each repository before
running the `svn2git` command. The conversion process will take some time.
```bash
svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
```
If your SVN repository requires a username and password add the
`--username <username>` and `--password <password` flags to the above command.
`svn2git` also supports excluding certain file paths, branches, tags, etc. See
[svn2git documentation](https://github.com/nirvdrum/svn2git) or run
`svn2git --help` for full documentation on all of the available options.
Create a new GitLab project, where you will eventually push your converted code.
Copy the SSH or HTTP(S) repository URL from the project page. Add the GitLab
repository as a Git remote and push all the changes. This will push all commits,
branches and tags.
```bash
git remote add origin git@gitlab.com:<group>/<project>.git
git push --all origin
git push --tags origin
```
## Contribute to this guide
We welcome all contributions that would expand this guide with instructions on
how to migrate from SVN and other version control systems.
module AfterCommitQueue
extend ActiveSupport::Concern
included do
after_commit :_run_after_commit_queue
after_rollback :_clear_after_commit_queue
end
def run_after_commit(method = nil, &block)
_after_commit_queue << proc { self.send(method) } if method
_after_commit_queue << block if block
true
end
protected
def _run_after_commit_queue
while action = _after_commit_queue.pop
self.instance_eval(&action)
end
end
def _after_commit_queue
@after_commit_queue ||= []
end
def _clear_after_commit_queue
_after_commit_queue.clear
end
end
...@@ -290,7 +290,7 @@ module API ...@@ -290,7 +290,7 @@ module API
def uploaded_file(field, uploads_path) def uploaded_file(field, uploads_path)
if params[field] if params[field]
bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename) bad_request!("#{field} is not a file") unless params[field][:filename]
return params[field] return params[field]
end end
......
...@@ -11,7 +11,7 @@ module API ...@@ -11,7 +11,7 @@ module API
def add_pagination_headers(paginated_data) def add_pagination_headers(paginated_data)
header 'X-Total', paginated_data.total_count.to_s header 'X-Total', paginated_data.total_count.to_s
header 'X-Total-Pages', paginated_data.total_pages.to_s header 'X-Total-Pages', total_pages(paginated_data).to_s
header 'X-Per-Page', paginated_data.limit_value.to_s header 'X-Per-Page', paginated_data.limit_value.to_s
header 'X-Page', paginated_data.current_page.to_s header 'X-Page', paginated_data.current_page.to_s
header 'X-Next-Page', paginated_data.next_page.to_s header 'X-Next-Page', paginated_data.next_page.to_s
...@@ -26,20 +26,25 @@ module API ...@@ -26,20 +26,25 @@ module API
links = [] links = []
request_params[:page] = paginated_data.current_page - 1 request_params[:page] = paginated_data.prev_page
links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") unless paginated_data.first_page? links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") if request_params[:page]
request_params[:page] = paginated_data.current_page + 1 request_params[:page] = paginated_data.next_page
links << %(<#{request_url}?#{request_params.to_query}>; rel="next") unless paginated_data.last_page? links << %(<#{request_url}?#{request_params.to_query}>; rel="next") if request_params[:page]
request_params[:page] = 1 request_params[:page] = 1
links << %(<#{request_url}?#{request_params.to_query}>; rel="first") links << %(<#{request_url}?#{request_params.to_query}>; rel="first")
request_params[:page] = paginated_data.total_pages request_params[:page] = total_pages(paginated_data)
links << %(<#{request_url}?#{request_params.to_query}>; rel="last") links << %(<#{request_url}?#{request_params.to_query}>; rel="last")
links.join(', ') links.join(', ')
end end
def total_pages(paginated_data)
# Ensure there is in total at least 1 page
[paginated_data.total_pages, 1].max
end
end end
end end
end end
...@@ -16,9 +16,9 @@ module API ...@@ -16,9 +16,9 @@ module API
case scope case scope
when String when String
[scope] [scope]
when Hashie::Mash when ::Hash
scope.values scope.values
when Hashie::Array when ::Array
scope scope
else else
['unknown'] ['unknown']
......
...@@ -57,7 +57,7 @@ module API ...@@ -57,7 +57,7 @@ module API
end end
get "templates/licenses" do get "templates/licenses" do
options = { options = {
featured: declared(params).popular.present? ? true : nil featured: declared(params)[:popular].present? ? true : nil
} }
licences = ::Kaminari.paginate_array(Licensee::License.all(options)) licences = ::Kaminari.paginate_array(Licensee::License.all(options))
present paginate(licences), with: Entities::RepoLicense present paginate(licences), with: Entities::RepoLicense
...@@ -71,7 +71,7 @@ module API ...@@ -71,7 +71,7 @@ module API
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
end end
get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do
not_found!('License') unless Licensee::License.find(declared(params).name) not_found!('License') unless Licensee::License.find(declared(params)[:name])
template = parsed_license_template template = parsed_license_template
...@@ -102,7 +102,7 @@ module API ...@@ -102,7 +102,7 @@ module API
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
end end
get "templates/#{template_type}/:name" do get "templates/#{template_type}/:name" do
new_template = klass.find(declared(params).name) new_template = klass.find(declared(params)[:name])
render_response(template_type, new_template) render_response(template_type, new_template)
end end
......
...@@ -16,7 +16,7 @@ module API ...@@ -16,7 +16,7 @@ module API
coerce_with: ->(scope) { coerce_with: ->(scope) {
if scope.is_a?(String) if scope.is_a?(String)
[scope] [scope]
elsif scope.is_a?(Hashie::Mash) elsif scope.is_a?(::Hash)
scope.values scope.values
else else
['unknown'] ['unknown']
......
...@@ -59,7 +59,7 @@ module API ...@@ -59,7 +59,7 @@ module API
end end
get route do get route do
options = { options = {
featured: declared(params).popular.present? ? true : nil featured: declared(params)[:popular].present? ? true : nil
} }
present Licensee::License.all(options), with: ::API::Entities::RepoLicense present Licensee::License.all(options), with: ::API::Entities::RepoLicense
end end
...@@ -76,7 +76,7 @@ module API ...@@ -76,7 +76,7 @@ module API
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
end end
get route, requirements: { name: /[\w\.-]+/ } do get route, requirements: { name: /[\w\.-]+/ } do
not_found!('License') unless Licensee::License.find(declared(params).name) not_found!('License') unless Licensee::License.find(declared(params)[:name])
template = parsed_license_template template = parsed_license_template
...@@ -111,7 +111,7 @@ module API ...@@ -111,7 +111,7 @@ module API
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
end end
get route do get route do
new_template = klass.find(declared(params).name) new_template = klass.find(declared(params)[:name])
render_response(template_type, new_template) render_response(template_type, new_template)
end end
......
...@@ -81,12 +81,15 @@ module Gitlab ...@@ -81,12 +81,15 @@ module Gitlab
relative_order: index relative_order: index
) )
# Compatibility with old diffs created with Psych.
diff_hash.tap do |hash| diff_hash.tap do |hash|
diff_text = hash[:diff] diff_text = hash[:diff]
hash[:too_large] = !!hash[:too_large] hash[:too_large] = !!hash[:too_large]
hash[:a_mode] ||= guess_mode(hash[:new_file], hash[:diff])
hash[:b_mode] ||= guess_mode(hash[:deleted_file], hash[:diff])
# Compatibility with old diffs created with Psych.
if diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only? if diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only?
hash[:binary] = true hash[:binary] = true
hash[:diff] = [diff_text].pack('m0') hash[:diff] = [diff_text].pack('m0')
...@@ -97,6 +100,15 @@ module Gitlab ...@@ -97,6 +100,15 @@ module Gitlab
[commit_rows, file_rows] [commit_rows, file_rows]
end end
# This doesn't have to be 100% accurate, because it's only used for
# display - it won't change file modes in the repository. Submodules are
# created as 600, regular files as 644.
def guess_mode(file_missing, diff)
return '0' if file_missing
diff.include?('Subproject commit') ? '160000' : '100644'
end
# Unlike MergeRequestDiff#valid_raw_diff?, don't count Rugged objects as # Unlike MergeRequestDiff#valid_raw_diff?, don't count Rugged objects as
# valid, because we don't render them usefully anyway. # valid, because we don't render them usefully anyway.
def valid_raw_diffs?(diffs) def valid_raw_diffs?(diffs)
......
...@@ -204,21 +204,26 @@ module Gitlab ...@@ -204,21 +204,26 @@ module Gitlab
# #
# name - The name of the tag as a String. # name - The name of the tag as a String.
def tag_exists?(name) def tag_exists?(name)
!!rugged.tags[name] gitaly_migrate(:ref_exists_tags) do |is_enabled|
if is_enabled
gitaly_ref_exists?("refs/tags/#{name}")
else
rugged_tag_exists?(name)
end
end
end end
# Returns true if the given branch exists # Returns true if the given branch exists
# #
# name - The name of the branch as a String. # name - The name of the branch as a String.
def branch_exists?(name) def branch_exists?(name)
rugged.branches.exists?(name) gitaly_migrate(:ref_exists_branches) do |is_enabled|
if is_enabled
# If the branch name is invalid (e.g. ".foo") Rugged will raise an error. gitaly_ref_exists?("refs/heads/#{name}")
# Whatever code calls this method shouldn't have to deal with that so else
# instead we just return `false` (which is true since a branch doesn't rugged_branch_exists?(name)
# exist when it has an invalid name). end
rescue Rugged::ReferenceError end
false
end end
# Returns an Array of branch and tag names # Returns an Array of branch and tag names
...@@ -653,33 +658,15 @@ module Gitlab ...@@ -653,33 +658,15 @@ module Gitlab
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/328 # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/328
def copy_gitattributes(ref) def copy_gitattributes(ref)
begin Gitlab::GitalyClient.migrate(:apply_gitattributes) do |is_enabled|
commit = lookup(ref) if is_enabled
rescue Rugged::ReferenceError gitaly_copy_gitattributes(ref)
raise InvalidRef.new("Ref #{ref} is invalid") else
end rugged_copy_gitattributes(ref)
end
# Create the paths
info_dir_path = File.join(path, 'info')
info_attributes_path = File.join(info_dir_path, 'attributes')
begin
# Retrieve the contents of the blob
gitattributes_content = blob_content(commit, '.gitattributes')
rescue InvalidBlobName
# No .gitattributes found. Should now remove any info/attributes and return
File.delete(info_attributes_path) if File.exist?(info_attributes_path)
return
end
# Create the info directory if needed
Dir.mkdir(info_dir_path) unless File.directory?(info_dir_path)
# Write the contents of the .gitattributes file to info/attributes
# Use binary mode to prevent Rails from converting ASCII-8BIT to UTF-8
File.open(info_attributes_path, "wb") do |file|
file.write(gitattributes_content)
end end
rescue GRPC::InvalidArgument
raise InvalidRef
end end
# Returns the Git attributes for the given file path. # Returns the Git attributes for the given file path.
...@@ -1012,6 +999,68 @@ module Gitlab ...@@ -1012,6 +999,68 @@ module Gitlab
raw_output.compact raw_output.compact
end end
# Returns true if the given ref name exists
#
# Ref names must start with `refs/`.
def gitaly_ref_exists?(ref_name)
gitaly_ref_client.ref_exists?(ref_name)
end
# Returns true if the given tag exists
#
# name - The name of the tag as a String.
def rugged_tag_exists?(name)
!!rugged.tags[name]
end
# Returns true if the given branch exists
#
# name - The name of the branch as a String.
def rugged_branch_exists?(name)
rugged.branches.exists?(name)
# If the branch name is invalid (e.g. ".foo") Rugged will raise an error.
# Whatever code calls this method shouldn't have to deal with that so
# instead we just return `false` (which is true since a branch doesn't
# exist when it has an invalid name).
rescue Rugged::ReferenceError
false
end
def gitaly_copy_gitattributes(revision)
gitaly_repository_client.apply_gitattributes(revision)
end
def rugged_copy_gitattributes(ref)
begin
commit = lookup(ref)
rescue Rugged::ReferenceError
raise InvalidRef.new("Ref #{ref} is invalid")
end
# Create the paths
info_dir_path = File.join(path, 'info')
info_attributes_path = File.join(info_dir_path, 'attributes')
begin
# Retrieve the contents of the blob
gitattributes_content = blob_content(commit, '.gitattributes')
rescue InvalidBlobName
# No .gitattributes found. Should now remove any info/attributes and return
File.delete(info_attributes_path) if File.exist?(info_attributes_path)
return
end
# Create the info directory if needed
Dir.mkdir(info_dir_path) unless File.directory?(info_dir_path)
# Write the contents of the .gitattributes file to info/attributes
# Use binary mode to prevent Rails from converting ASCII-8BIT to UTF-8
File.open(info_attributes_path, "wb") do |file|
file.write(gitattributes_content)
end
end
end end
end end
end end
...@@ -13,10 +13,17 @@ module Gitlab ...@@ -13,10 +13,17 @@ module Gitlab
) )
response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request) response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_blob, request)
blob = response.first data = ''
return unless blob.oid.present? blob = nil
response.each do |msg|
if blob.nil?
blob = msg
end
data = response.reduce(blob.data.dup) { |memo, msg| memo << msg.data.dup } data << msg.data
end
return nil if blob.oid.blank?
Gitlab::Git::Blob.new( Gitlab::Git::Blob.new(
id: blob.oid, id: blob.oid,
......
...@@ -60,15 +60,21 @@ module Gitlab ...@@ -60,15 +60,21 @@ module Gitlab
) )
response = GitalyClient.call(@repository.storage, :commit_service, :tree_entry, request) response = GitalyClient.call(@repository.storage, :commit_service, :tree_entry, request)
entry = response.first
return unless entry.oid.present?
if entry.type == :BLOB entry = nil
rest_of_data = response.reduce("") { |memo, msg| memo << msg.data } data = ''
entry.data += rest_of_data response.each do |msg|
if entry.nil?
entry = msg
break unless entry.type == :BLOB
end
data << msg.data
end end
entry.data = data
entry entry unless entry.oid.blank?
end end
def tree_entries(repository, revision, path) def tree_entries(repository, revision, path)
......
...@@ -70,6 +70,14 @@ module Gitlab ...@@ -70,6 +70,14 @@ module Gitlab
consume_tags_response(response) consume_tags_response(response)
end end
def ref_exists?(ref_name)
request = Gitaly::RefExistsRequest.new(repository: @gitaly_repo, ref: ref_name)
response = GitalyClient.call(@storage, :ref_service, :ref_exists, request)
response.value
rescue GRPC::InvalidArgument => e
raise ArgumentError, e.message
end
private private
def consume_refs_response(response) def consume_refs_response(response)
......
...@@ -32,6 +32,11 @@ module Gitlab ...@@ -32,6 +32,11 @@ module Gitlab
request = Gitaly::RepositorySizeRequest.new(repository: @gitaly_repo) request = Gitaly::RepositorySizeRequest.new(repository: @gitaly_repo)
GitalyClient.call(@storage, :repository_service, :repository_size, request).size GitalyClient.call(@storage, :repository_service, :repository_size, request).size
end end
def apply_gitattributes(revision)
request = Gitaly::ApplyGitattributesRequest.new(repository: @gitaly_repo, revision: revision)
GitalyClient.call(@storage, :repository_service, :apply_gitattributes, request)
end
end end
end end
end end
module Gitlab module Gitlab
module ImportExport module ImportExport
class ProjectTreeRestorer class ProjectTreeRestorer
# Relations which cannot have both group_id and project_id at the same time
RESTRICT_PROJECT_AND_GROUP = %i(milestones).freeze
def initialize(user:, shared:, project:) def initialize(user:, shared:, project:)
@path = File.join(shared.export_path, 'project.json') @path = File.join(shared.export_path, 'project.json')
@user = user @user = user
...@@ -118,9 +121,11 @@ module Gitlab ...@@ -118,9 +121,11 @@ module Gitlab
end end
def create_relation(relation, relation_hash_list) def create_relation(relation, relation_hash_list)
relation_type = relation.to_sym
relation_array = [relation_hash_list].flatten.map do |relation_hash| relation_array = [relation_hash_list].flatten.map do |relation_hash|
Gitlab::ImportExport::RelationFactory.create(relation_sym: relation.to_sym, Gitlab::ImportExport::RelationFactory.create(relation_sym: relation_type,
relation_hash: parsed_relation_hash(relation_hash), relation_hash: parsed_relation_hash(relation_hash, relation_type),
members_mapper: members_mapper, members_mapper: members_mapper,
user: @user, user: @user,
project: restored_project) project: restored_project)
...@@ -129,8 +134,16 @@ module Gitlab ...@@ -129,8 +134,16 @@ module Gitlab
relation_hash_list.is_a?(Array) ? relation_array : relation_array.first relation_hash_list.is_a?(Array) ? relation_array : relation_array.first
end end
def parsed_relation_hash(relation_hash) def parsed_relation_hash(relation_hash, relation_type)
relation_hash.merge!('group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id) if RESTRICT_PROJECT_AND_GROUP.include?(relation_type)
params = {}
params['group_id'] = restored_project.group.try(:id) if relation_hash['group_id']
params['project_id'] = restored_project.id if relation_hash['project_id']
else
params = { 'group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id }
end
relation_hash.merge(params)
end end
end end
end end
......
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