Commit cb659e4c authored by Timm Drevensek's avatar Timm Drevensek

Merge branch 'master' into request/relative_submodules

Conflicts:
	CHANGELOG
parents ea82bcd3 3b0510a7
v 6.8.0 v 6.8.0
- Ability to at mention users that are participating in issue and merge req. discussion - Ability to at mention users that are participating in issue and merge req. discussion
- Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu) - Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu)
- Add support for relative submodules - Make user search case-insensitive (Christopher Arnold)
- Remove omniauth-ldap nickname bug workaround
- Drop all tables before restoring a Postgres backup
- Make the repository downloads path configurable
- Create branches via API (sponsored by O'Reilly Media)
v 6.7.2 v 6.7.2
- Fix upgrader script - Fix upgrader script
......
...@@ -63,10 +63,11 @@ If you can, please submit a merge request with the fix or improvements including ...@@ -63,10 +63,11 @@ If you can, please submit a merge request with the fix or improvements including
1. Add your changes to the [CHANGELOG](CHANGELOG) 1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) 1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. Push the commit to your fork 1. Push the commit to your fork
1. Submit a merge request (MR) 1. Submit a merge request (MR) to the master branch
1. The MR title should describes the change you want to make 1. The MR title should describes the change you want to make
1. The MR description should give a motive for your change and the method you used to achieve it 1. The MR description should give a motive for your change and the method you used to achieve it
1. If the MR changes the UI it should include before and after screenshots 1. If the MR changes the UI it should include before and after screenshots
1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R`
1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR 1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR
1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
......
# A sample Guardfile # A sample Guardfile
# More info at https://github.com/guard/guard#readme # More info at https://github.com/guard/guard#readme
guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_pass: false do
watch(%r{^spec/.+_spec\.rb$}) watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" } watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
......
...@@ -113,38 +113,10 @@ or start each component separately ...@@ -113,38 +113,10 @@ or start each component separately
Single Spinach test: bundle exec spinach features/project/issues/milestones.feature Single Spinach test: bundle exec spinach features/project/issues/milestones.feature
### GitLab interfaces ### Documentation
* [GitLab API doc](doc/api/README.md) or see the [GitLab API website](http://api.gitlab.org/) All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/).
* [Rake tasks](doc/raketasks) including a [backup and restore procedure](doc/raketasks/backup_restore.md)
* [Directory structure](doc/install/structure.md)
* [Database installation](doc/install/databases.md)
* [Markdown specification](doc/markdown/markdown.md)
* [Security guide](doc/security/rack_attack.md) to throttle abusive requests
### Getting help ### Getting help
* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported. Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.
* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix.
* [Feature request forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
* [Contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) describes how to submit merge requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.
* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.
* [#gitlab IRC channel](http://www.freenode.net/) on Freenode to get in touch with other GitLab users and get help, it's managed by James Newton (newton), Drew Blessing (dblessing), and Sam Gleske (sag47).
* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help.
...@@ -13,6 +13,12 @@ ...@@ -13,6 +13,12 @@
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05); border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:after {
content: " ";
display: table;
clear: both;
}
&.disabled { &.disabled {
color: #888; color: #888;
} }
...@@ -46,6 +52,12 @@ ...@@ -46,6 +52,12 @@
.author { color: #999; } .author { color: #999; }
.list-item-name {
float: left;
position: relative;
top: 3px;
}
p { p {
padding-top: 1px; padding-top: 1px;
margin: 0; margin: 0;
......
...@@ -113,6 +113,5 @@ ...@@ -113,6 +113,5 @@
float: left; float: left;
margin-right: 3px; margin-right: 3px;
color: #999; color: #999;
margin-bottom: 10px;
width: 16px; width: 16px;
} }
...@@ -62,6 +62,29 @@ ...@@ -62,6 +62,29 @@
font-size: 12px; font-size: 12px;
} }
} }
.text-file-parallel div {
display: inline-block;
padding-bottom: 16px;
}
.diff-side {
overflow-x: scroll;
width: 508px;
height: 700px;
}
.diff-side.diff-side-left{
overflow-y:hidden;
}
.diff-side table, td.diff-middle table {
height: 700px;
}
.diff-middle {
width: 114px;
vertical-align: top;
height: 700px;
overflow: hidden
}
.old_line, .new_line, .diff_line { .old_line, .new_line, .diff_line {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
...@@ -125,8 +148,6 @@ ...@@ -125,8 +148,6 @@
} }
&.parallel { &.parallel {
display: table-cell; display: table-cell;
overflow: hidden;
width: 50%;
} }
} }
} }
......
...@@ -16,11 +16,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -16,11 +16,7 @@ class Projects::BranchesController < Projects::ApplicationController
end end
def create def create
@repository.add_branch(params[:branch_name], params[:ref]) CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user)
if new_branch = @repository.find_branch(params[:branch_name])
Event.create_ref_event(@project, current_user, new_branch, 'add')
end
redirect_to project_branches_path(@project) redirect_to project_branches_path(@project)
end end
......
...@@ -14,7 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -14,7 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
render_404 and return render_404 and return
end end
storage_path = Rails.root.join("tmp", "repositories") storage_path = Gitlab.config.gitlab.repository_downloads_path
file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase) file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase)
......
...@@ -105,8 +105,80 @@ module CommitsHelper ...@@ -105,8 +105,80 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end end
def get_old_file(project, commit, diff) def parallel_diff_lines(project, commit, diff, file)
project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
deleted_lines = {}
added_lines = {}
each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
if type == "old"
deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
elsif type == "new"
added_lines[line_new] = { line_code: line_code, type: type, line: line }
end
end
max_length = old_file ? old_file.sloc + added_lines.length : file.sloc
offset1 = 0
offset2 = 0
old_lines = []
new_lines = []
max_length.times do |line_index|
line_index1 = line_index - offset1
line_index2 = line_index - offset2
deleted_line = deleted_lines[line_index1 + 1]
added_line = added_lines[line_index2 + 1]
old_line = old_file.lines[line_index1] if old_file
new_line = file.lines[line_index2]
if deleted_line && added_line
elsif deleted_line
new_line = nil
offset2 += 1
elsif added_line
old_line = nil
offset1 += 1
end
old_lines[line_index] = DiffLine.new
new_lines[line_index] = DiffLine.new
# old
if line_index == 0 && diff.new_file
old_lines[line_index].type = :file_created
old_lines[line_index].content = 'File was created'
elsif deleted_line
old_lines[line_index].type = :deleted
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
old_lines[line_index].code = deleted_line[:line_code]
elsif old_line
old_lines[line_index].type = :no_change
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
else
old_lines[line_index].type = :added
end
# new
if line_index == 0 && diff.deleted_file
new_lines[line_index].type = :file_deleted
new_lines[line_index].content = "File was deleted"
elsif added_line
new_lines[line_index].type = :added
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
new_lines[line_index].code = added_line[:line_code]
elsif new_line
new_lines[line_index].type = :no_change
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
else
new_lines[line_index].type = :deleted
end
end
return old_lines, new_lines
end end
protected protected
......
class DiffLine
attr_accessor :type, :content, :num, :code
end
...@@ -204,7 +204,7 @@ class User < ActiveRecord::Base ...@@ -204,7 +204,7 @@ class User < ActiveRecord::Base
end end
def search query def search query
where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
end end
def by_username_or_id(name_or_id) def by_username_or_id(name_or_id)
......
class CreateBranchService
def execute(project, branch_name, ref, current_user)
repository = project.repository
repository.add_branch(branch_name, ref)
new_branch = repository.find_branch(branch_name)
if new_branch
Event.create_ref_event(project, current_user, new_branch, 'add')
end
new_branch
end
end
...@@ -178,21 +178,41 @@ class NotificationService ...@@ -178,21 +178,41 @@ class NotificationService
# Get project users with WATCH notification level # Get project users with WATCH notification level
def project_watchers(project) def project_watchers(project)
project_watchers = [] # Gather all user ids that have WATCH notification setting for project
member_methods = { project => :users_projects } project_notification_uids = project_notification_list(project, Notification::N_WATCH)
member_methods.merge!(project.group => :users_groups) if project.group
member_methods.each do |object, member_method| # Gather all user ids that have WATCH notification setting for group
# Get project notification settings since it has higher priority group_notification_uids = group_notification_list(project, Notification::N_WATCH)
user_ids = object.send(member_method).where(notification_level: Notification::N_WATCH).pluck(:user_id)
project_watchers += User.where(id: user_ids)
# next collect users who use global settings with watch state # Gather all user ids that have GLOBAL setting
user_ids = object.send(member_method).where(notification_level: Notification::N_GLOBAL).pluck(:user_id) global_notification_uids = global_notification_list(project)
project_watchers += User.where(id: user_ids, notification_level: Notification::N_WATCH)
project_and_group_uids = [project_notification_uids, group_notification_uids].flatten.uniq
group_and_project_watchers = User.where(id: project_and_group_uids)
# Find all users that have WATCH as their GLOBAL setting
global_watchers = User.where(id: global_notification_uids, notification_level: Notification::N_WATCH)
[group_and_project_watchers, global_watchers].flatten.uniq
end
def project_notification_list(project, notification_level)
project.users_projects.where(notification_level: notification_level).pluck(:user_id)
end
def group_notification_list(project, notification_level)
if project.group
project.group.users_groups.where(notification_level: notification_level).pluck(:user_id)
else
[]
end
end end
project_watchers.uniq def global_notification_list(project)
[
project_notification_list(project, Notification::N_GLOBAL),
group_notification_list(project, Notification::N_GLOBAL)
].flatten
end end
# Remove users with disabled notifications from array # Remove users with disabled notifications from array
......
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
- @group.users_groups.order('group_access DESC').each do |member| - @group.users_groups.order('group_access DESC').each do |member|
- user = member.user - user = member.user
%li{class: dom_class(user)} %li{class: dom_class(user)}
.list-item-name
%strong %strong
= link_to user.name, admin_user_path(user) = link_to user.name, admin_user_path(user)
%span.pull-right.light %span.pull-right.light
......
...@@ -28,8 +28,10 @@ ...@@ -28,8 +28,10 @@
%ul.well-list %ul.well-list
- @hooks.each do |hook| - @hooks.each do |hook|
%li %li
.list-item-name
= link_to admin_hook_path(hook) do
%strong= hook.url
.pull-right .pull-right
= link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small" = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small"
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-small" = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-small"
= link_to admin_hook_path(hook) do
%strong= hook.url
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
%ul.well-list %ul.well-list
- @projects.each do |project| - @projects.each do |project|
%li %li
.list-item-name
%span{ class: visibility_level_color(project.visibility_level) } %span{ class: visibility_level_color(project.visibility_level) }
= visibility_level_icon(project.visibility_level) = visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, [:admin, project] = link_to project.name_with_namespace, [:admin, project]
......
...@@ -116,6 +116,7 @@ ...@@ -116,6 +116,7 @@
- @project.users_projects.each do |users_project| - @project.users_projects.each do |users_project|
- user = users_project.user - user = users_project.user
%li.users_project %li.users_project
.list-item-name
%strong %strong
= link_to user.name, admin_user_path(user) = link_to user.name, admin_user_path(user)
.pull-right .pull-right
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
%ul.well-list %ul.well-list
- @users.each do |user| - @users.each do |user|
%li %li
.list-item-name
- if user.blocked? - if user.blocked?
%i.icon-lock.cred %i.icon-lock.cred
- else - else
......
...@@ -124,6 +124,7 @@ ...@@ -124,6 +124,7 @@
- @user.users_groups.each do |user_group| - @user.users_groups.each do |user_group|
- group = user_group.group - group = user_group.group
%li.users_group %li.users_group
%span{class: ("list-item-name" unless user_group.owner?)}
%strong= link_to group.name, admin_group_path(group) %strong= link_to group.name, admin_group_path(group)
.pull-right .pull-right
%span.light= user_group.human_access %span.light= user_group.human_access
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
%ul.well-list %ul.well-list
- @group.projects.each do |project| - @group.projects.each do |project|
%li %li
.list-item-name
= visibility_level_icon(project.visibility_level) = visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project = link_to project.name_with_namespace, project
.pull-right .pull-right
......
...@@ -23,4 +23,4 @@ ...@@ -23,4 +23,4 @@
- if @project - if @project
You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team. You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team.
- if @target_url - if @target_url
#{link_to "View in GitLab", @target_url} #{link_to "View it on GitLab", @target_url}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
%li %li
#{commit.short_id} - #{commit.title} #{commit.short_id} - #{commit.title}
%h4 Diff: %h4 Changes:
- @diffs.each do |diff| - @diffs.each do |diff|
%li %li
%strong %strong
...@@ -23,6 +23,6 @@ ...@@ -23,6 +23,6 @@
%br %br
- if @compare.timeout - if @compare.timeout
%h5 Huge diff. To prevent performance issues it was hidden %h5 To prevent performance issues changes are hidden
- elsif @compare.commits_over_limit? - elsif @compare.commits_over_limit?
%h5 Diff for big amount of commits is disabled %h5 Changes are not shown due to large amount of commits
...@@ -6,7 +6,7 @@ Commits: ...@@ -6,7 +6,7 @@ Commits:
#{commit.short_id} - #{truncate(commit.title, length: 40)} #{commit.short_id} - #{truncate(commit.title, length: 40)}
\ \
\ \
Diff: Changes:
- @diffs.each do |diff| - @diffs.each do |diff|
\ \
\===================================== \=====================================
...@@ -22,4 +22,4 @@ Diff: ...@@ -22,4 +22,4 @@ Diff:
- if @compare.timeout - if @compare.timeout
Huge diff. To prevent performance issues it was hidden Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit? - elsif @compare.commits_over_limit?
Diff for big amount of commits is disabled Changes are not shown due to large amount of commits
/ Side-by-side diff view / Side-by-side diff view
- old_file = get_old_file(project, @commit, diff) - old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- deleted_lines = {} - num_lines = old_lines.length
- added_lines = {}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
- if type == "old"
- deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
- elsif type == "new"
- added_lines[line_new] = { line_code: line_code, type: type, line: line }
- max_length = old_file.sloc + added_lines.length if old_file
- max_length ||= file.sloc
- offset1 = 0
- offset2 = 0
%div.text-file-parallel %div.text-file-parallel
%table{ style: "table-layout: fixed;" } %div.diff-side.diff-side-left
- max_length.times do |line_index| %table
- line_index1 = line_index - offset1 - old_lines.each do |line|
- line_index2 = line_index - offset2
- deleted_line = deleted_lines[line_index1 + 1]
- added_line = added_lines[line_index2 + 1]
- old_line = old_file.lines[line_index1] if old_file
- new_line = file.lines[line_index2]
- if deleted_line && added_line
- elsif deleted_line
- new_line = nil
- offset2 += 1
- elsif added_line
- old_line = nil
- offset1 += 1
%tr.line_holder.parallel %tr.line_holder.parallel
- if line_index == 0 && diff.new_file - if line.type == :file_created
%td.line_content.parallel= "File was created" %td.line_content.parallel= "File was created"
%td.old_line= "" - elsif line.type == :deleted
- elsif deleted_line %td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
%td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line - else line.type == :no_change
%td.old_line.old %td.line_content.parallel= line.content
= line_index1 + 1
- if @comments_allowed %div.diff-middle
=# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code] %table
- elsif old_line - num_lines.times do |index|
%td.line_content.parallel= old_line %tr
%td.old_line= line_index1 + 1 - if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else - else
%td.line_content.parallel= "" %td.old_line= old_lines[index].num
%td.old_line= ""
%td.diff_line= "" %td.diff_line=""
- if diff.deleted_file && line_index == 0 - if new_lines[index].type == :added
%td.new_line= "" %td.new_line.new= new_lines[index].num
%td.line_content.parallel= "File was deleted"
- elsif added_line
%td.new_line.new
= line_index2 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: added_line[:line_code]
%td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line
- elsif new_line
%td.new_line= line_index2 + 1
%td.line_content.parallel= new_line
- else - else
%td.new_line= "" %td.new_line= new_lines[index].num
%td.line_content.parallel= ""
%div.diff-side.diff-side-right
%table
- new_lines.each do |line|
%tr.line_holder.parallel
- if line.type == :file_deleted
%td.line_content.parallel= "File was deleted"
- elsif line.type == :added
%td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
:javascript
$('.diff-side-right').on('scroll', function(){
$('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
$('.diff-side-left').scrollLeft($(this).scrollLeft());
});
- if @reply_allowed $('.diff-side-left').on('scroll', function(){
- comments1 = [] $('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
- comments2 = [] $('.diff-side-right').scrollLeft($(this).scrollLeft());
- comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line });
- comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line
- unless comments1.empty? && comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line
\ No newline at end of file
- too_big = diff.diff.lines.count > 1000 - too_big = diff.diff.lines.count > 1000
- if too_big - if too_big
%a.supp_diff_link Diff suppressed. Click to show %a.supp_diff_link Changes suppressed. Click to show
%table.text-file{class: "#{'hide' if too_big}"} %table.text-file{class: "#{'hide' if too_big}"}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
......
...@@ -18,17 +18,17 @@ ...@@ -18,17 +18,17 @@
- else - else
%ul.well-list= render Commit.decorate(@commits), project: @project %ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Diff %h4 Changes
- if @diffs.present? - if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project = render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger .bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line diff is not shown. %p To preserve performance the line changes are not shown.
- elsif @timeout - elsif @timeout
.bs-callout.bs-callout-danger .bs-callout.bs-callout-danger
%h4 Diff for this comparison is extremely large. %h4 Number of changed files for this comparison is extremely large.
%p Use command line to browse diff for this comparison. %p Use command line to browse through changes for this comparison.
- else - else
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Deploy key: Deploy key:
= @key.title = @key.title
%small %small
created at created on
= @key.created_at.stamp("Aug 21, 2011") = @key.created_at.stamp("Aug 21, 2011")
.back-link .back-link
= link_to project_deploy_keys_path(@project) do = link_to project_deploy_keys_path(@project) do
......
%div.issue-form-holder %div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
%hr %hr
- if @repository.contribution_guide && !@issue.persisted? - if !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2 .alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
%li.diffs-tab{data: {action: 'diffs'}} %li.diffs-tab{data: {action: 'diffs'}}
= link_to diffs_project_merge_request_path(@project, @merge_request) do = link_to diffs_project_merge_request_path(@project, @merge_request) do
%i.icon-list-alt %i.icon-list-alt
Diff Changes
- content_for :note_actions do - content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- else - else
.bs-callout.bs-callout-warning .bs-callout.bs-callout-warning
%h4 %h4
Diff for this comparison is extremely large. Changes view for this comparison is extremely large.
%p %p
You can You can
= link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink" = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
%strong Archived projects cannot be committed to! %strong Archived projects cannot be committed to!
- else - else
.bs-callout .bs-callout
%strong You don't have permission to merge this MR .automerge_widget.cannot_be_merged.hide
%strong This can't be merged automatically, even if it could be merged you don't have the permission to do so.
.automerge_widget.can_be_merged.hide
%strong This can be merged automatically but you don't have the permission to do so.
- if @show_merge_controls - if @show_merge_controls
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
- return unless user - return unless user
- show_roles = true if show_roles.nil? - show_roles = true if show_roles.nil?
%li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
%span{class: ("list-item-name" if show_controls)}
= image_tag avatar_icon(user.email, 16), class: "avatar s16" = image_tag avatar_icon(user.email, 16), class: "avatar s16"
%strong= user.name %strong= user.name
%span.cgray= user.username %span.cgray= user.username
......
...@@ -76,6 +76,11 @@ production: &base ...@@ -76,6 +76,11 @@ production: &base
snippets: false snippets: false
visibility_level: "private" # can be "private" | "internal" | "public" visibility_level: "private" # can be "private" | "internal" | "public"
## Repository downloads directory
# When a user clicks e.g. 'Download zip' on a project, a temporary zip file is created in the following directory.
# The default is 'tmp/repositories' relative to the root of the Rails app.
# repository_downloads_path: tmp/repositories
## External issues trackers ## External issues trackers
issues_tracker: issues_tracker:
# redmine: # redmine:
......
...@@ -97,6 +97,7 @@ Settings.gitlab.default_projects_features['wiki'] = true if Settings.g ...@@ -97,6 +97,7 @@ Settings.gitlab.default_projects_features['wiki'] = true if Settings.g
Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil?
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root)
# #
# Gravatar # Gravatar
......
## The GitLab Documentation covers the following subjects
+ [API](api/README.md)
+ [Development](development/README.md)
+ [Install](install/README.md)
+ [Integration](integration/external-issue-tracker.md)
+ [Legal](legal/README.md)
+ [Markdown](markdown/markdown.md)
+ [Permissions](permissions/permissions.md)
+ [Public access](public_access/public_access.md)
+ [Raketasks](raketasks/README.md)
+ [Release](release/README.md)
+ [Security](security/README.md)
+ [SSH](ssh/README.md)
+ [System hooks](system_hooks/system_hooks.md)
+ [Update](update/README.md)
+ [Web hooks](web_hooks/web_hooks.md)
+ [Workflow](workflow/workflow.md)
# GitLab API # GitLab API
## End-points
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Branches](branches.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
## Introduction
All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile. All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile.
If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
...@@ -103,6 +130,10 @@ When listing resources you can pass the following parameters: ...@@ -103,6 +130,10 @@ When listing resources you can pass the following parameters:
+ `page` (default: `1`) - page number + `page` (default: `1`) - page number
+ `per_page` (default: `20`, max: `100`) - number of items to list per page + `per_page` (default: `20`, max: `100`) - number of items to list per page
[Link headers](http://www.w3.org/wiki/LinkHeader) are send back with each response.
These have `rel` prev/next/first/last and contain the relevant url.
Please use these instead of generating your own urls.
## id vs iid ## id vs iid
When you work with API you may notice two similar fields in api entites: id and iid. When you work with API you may notice two similar fields in api entites: id and iid.
...@@ -117,30 +148,3 @@ Issue ...@@ -117,30 +148,3 @@ Issue
So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json` So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
But when you want to create a link to web page - use `http:://host/project/issues/:iid.json` But when you want to create a link to web page - use `http:://host/project/issues/:iid.json`
## Contents
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
# Branches
## List repository branches
Get a list of repository branches from a project, sorted by name alphabetically.
```
GET /projects/:id/repository/branches
```
Parameters:
+ `id` (required) - The ID of a project
```json
[
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": true
}
]
```
## Get single repository branch
Get a single project repository branch.
```
GET /projects/:id/repository/branches/:branch
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": true
}
```
## Protect repository branch
Protects a single project repository branch. This is an idempotent function, protecting an already
protected repository branch still returns a `200 Ok` status code.
```
PUT /projects/:id/repository/branches/:branch/protect
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": true
}
```
## Unprotect repository branch
Unprotects a single project repository branch. This is an idempotent function, unprotecting an already
unprotected repository branch still returns a `200 Ok` status code.
```
PUT /projects/:id/repository/branches/:branch/unprotect
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": false
}
```
## Create repository branch
```
POST /projects/:id/repository/branches
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch_name` (required) - The name of the branch
+ `ref` (required) - Create branch from commit sha or existing branch
```json
{
"name": "my-new-branch",
"commit": {
"id": "8848c0e90327a0b70f1865b843fb2fbfb9345e57",
"message": "Merge pull request #54 from brightbox/use_fog_brightbox_module\n\nUpdate to use fog-brightbox module",
"parent_ids": ["fff449e0bf453576f16c91d6544f00a2664009d8", "f93a93626fec20fd659f4ed3ab2e64019b6169ae"],
"authored_date": "2014-02-20T19:54:55+02:00",
"author_name": "john smith",
"author_email": "john@example.com",
"committed_date": "2014-02-20T19:54:55+02:00",
"committer_name": "john smith",
"committer_email": "john@example.com"
},
"protected": false
}
```
## List repository branches
Get a list of repository branches from a project, sorted by name alphabetically.
```
GET /projects/:id/repository/branches
```
Parameters:
+ `id` (required) - The ID of a project
```json
[
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": true
}
]
```
## Get single repository branch
Get a single project repository branch.
```
GET /projects/:id/repository/branches/:branch
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": true
}
```
## Protect repository branch
Protects a single project repository branch. This is an idempotent function, protecting an already
protected repository branch still returns a `200 Ok` status code.
```
PUT /projects/:id/repository/branches/:branch/protect
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": true
}
```
## Unprotect repository branch
Unprotects a single project repository branch. This is an idempotent function, unprotecting an already
unprotected repository branch still returns a `200 Ok` status code.
```
PUT /projects/:id/repository/branches/:branch/unprotect
```
Parameters:
+ `id` (required) - The ID of a project
+ `branch` (required) - The name of the branch
```json
{
"name": "master",
"commit": {
"id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
"parents": [
{
"id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
}
],
"tree": "46e82de44b1061621357f24c05515327f2795a95",
"message": "add projects API",
"author": {
"name": "John Smith",
"email": "john@example.com"
},
"committer": {
"name": "John Smith",
"email": "john@example.com"
},
"authored_date": "2012-06-27T05:51:39-07:00",
"committed_date": "2012-06-28T03:44:20-07:00"
},
"protected": false
}
```
## List project repository tags ## List project repository tags
Get a list of repository tags from a project, sorted by name in reverse alphabetical order. Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
......
...@@ -51,6 +51,10 @@ GET /users ...@@ -51,6 +51,10 @@ GET /users
] ]
``` ```
You can search for a users by email or username with:
`/users?search=John`
Also see `def search query` in `app/models/user.rb`.
## Single user ## Single user
......
+ [Architecture](architecture.md)
+ [Shell commands](shell_commands.md)
...@@ -28,7 +28,7 @@ To serve repositories over SSH there's an add-on application called gitlab-shell ...@@ -28,7 +28,7 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
## Components ## Components
![GitLab Diagram Overview](resources/gitlab_diagram_overview.png "GitLab Diagram Overview") ![GitLab Diagram Overview](resources/gitlab_diagram_overview.png)
A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS. A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS.
It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server.
......
+ [Installation](installation.md)
+ [Requirements](requirements.md)
+ [Structure](structure.md)
+ [Database MySQL](database_mysql.md)
...@@ -7,6 +7,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se ...@@ -7,6 +7,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Install the database packages # Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
# Ensure you have MySQL version 5.5.14 or later
mysql --version
# Pick a database root password (can be anything), type it and press enter # Pick a database root password (can be anything), type it and press enter
# Retype the database root password and press enter # Retype the database root password and press enter
...@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se ...@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# change $password in the command below to a real password you pick # change $password in the command below to a real password you pick
mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
# Ensure you can use the InnoDB engine which is necessary to support long indexes.
# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
mysql> SET storage_engine=INNODB;
# Create the GitLab production database # Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
......
+ [Corporate contributor license agreement](corporate_contributor_license_agreement.md)
+ [Individual contributor license agreement](individual_contributor_license_agreement.md)
...@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions. ...@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions.
|------|-----|--------|---------|------|-----| |------|-----|--------|---------|------|-----|
|Browse group|✓|✓|✓|✓|✓| |Browse group|✓|✓|✓|✓|✓|
|Edit group|||||✓| |Edit group|||||✓|
|create project in group|||||✓| |Create project in group|||||✓|
|Manage group members|||||✓| |Manage group members|||||✓|
|Remove group|||||✓| |Remove group|||||✓|
......
+ [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md)
+ [Features](features.md)
+ [Maintenance](maintenance.md)
+ [User management](user_management.md)
+ [Web hooks](web_hooks.md)
+ [Monthly](monthly.md)
+ [Security](security.md)
+ [Password length limits](password_length_limits.md)
+ [Rack attack](rack_attack.md)
+ [Deploy keys](deploy_keys.md)
+ [SSH](ssh.md)
...@@ -9,4 +9,4 @@ After this the machine that uses the corresponding private key has read-only acc ...@@ -9,4 +9,4 @@ After this the machine that uses the corresponding private key has read-only acc
You can't add the same deploy key twice with the 'New Deploy Key' option. You can't add the same deploy key twice with the 'New Deploy Key' option.
If you want to add the same key to another project please enable it in the list that says 'Deploy keys from projects available to you'. If you want to add the same key to another project please enable it in the list that says 'Deploy keys from projects available to you'.
You need to be the owner of the deploy key to see it in this list. All the deploy keys of all the projects you have access to are available. This project access can happen through being a direct member of the project or through a group. See `def accessible_deploy_keys` in `app/models/user.rb` for more information.
...@@ -140,3 +140,6 @@ Follow the [`upgrade guide from 5.4 to 6.0`](5.4-to-6.0.md), except for the data ...@@ -140,3 +140,6 @@ Follow the [`upgrade guide from 5.4 to 6.0`](5.4-to-6.0.md), except for the data
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
``` ```
## Login issues after upgrade?
If running in https mode, be sure to read [Can't Verify csrf token authenticity](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide#cant-verify-csrf-token-authenticitycant-get-past-login-pageredirected-to-login-page)
+ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update)
+ [Uprader](upgrader.md)
+ [Ruby](ruby.md)
+ [Patch versions](patch_versions.md)
+ [MySQL to PostgreSQL](mysql_to_postgresql.md)
...@@ -3,13 +3,14 @@ ...@@ -3,13 +3,14 @@
```bash ```bash
git clone git@example.com:project-name.git git clone git@example.com:project-name.git
``` ```
2. Create branch with your feature 2. Create branch with your feature
```bash ```bash
git checkout -b $feature_name git checkout -b $feature_name
``` ```
3. Write code. Comit changes 3. Write code. Commit changes
```bash ```bash
git commit -am "My feature is ready" git commit -am "My feature is ready"
...@@ -21,6 +22,6 @@ ...@@ -21,6 +22,6 @@
git push origin $feature_name git push origin $feature_name
``` ```
5. Review your code on Commits page 5. Review your code on commits page
6. Create a merge request 6. Create a merge request
7. Your team lead will review the code &amp; merge it to the main branch 7. Your team lead will review the code &amp; merge it to the main branch
...@@ -45,5 +45,6 @@ module API ...@@ -45,5 +45,6 @@ module API
mount Files mount Files
mount Commits mount Commits
mount Namespaces mount Namespaces
mount Branches
end end
end end
require 'mime/types'
module API
# Projects API
class Branches < Grape::API
before { authenticate! }
before { authorize! :download_code, user_project }
resource :projects do
# Get a project repository branches
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/repository/branches
get ":id/repository/branches" do
present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project
end
# Get a single branch
#
# Parameters:
# id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# GET /projects/:id/repository/branches/:branch
get ":id/repository/branches/:branch" do
@branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
not_found!("Branch does not exist") if @branch.nil?
present @branch, with: Entities::RepoObject, project: user_project
end
# Protect a single branch
#
# Parameters:
# id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# PUT /projects/:id/repository/branches/:branch/protect
put ":id/repository/branches/:branch/protect" do
authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch])
not_found! unless @branch
protected_branch = user_project.protected_branches.find_by(name: @branch.name)
user_project.protected_branches.create(name: @branch.name) unless protected_branch
present @branch, with: Entities::RepoObject, project: user_project
end
# Unprotect a single branch
#
# Parameters:
# id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# PUT /projects/:id/repository/branches/:branch/unprotect
put ":id/repository/branches/:branch/unprotect" do
authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch])
not_found! unless @branch
protected_branch = user_project.protected_branches.find_by(name: @branch.name)
protected_branch.destroy if protected_branch
present @branch, with: Entities::RepoObject, project: user_project
end
# Create branch
#
# Parameters:
# id (required) - The ID of a project
# branch_name (required) - The name of the branch
# ref (required) - Create branch from commit sha or existing branch
# Example Request:
# POST /projects/:id/repository/branches
post ":id/repository/branches" do
authorize_push_project
@branch = CreateBranchService.new.execute(user_project, params[:branch_name], params[:ref], current_user)
present @branch, with: Entities::RepoObject, project: user_project
end
end
end
end
require 'mime/types' require 'mime/types'
module API module API
# Projects API # Projects commits API
class Commits < Grape::API class Commits < Grape::API
before { authenticate! } before { authenticate! }
before { authorize! :download_code, user_project } before { authorize! :download_code, user_project }
resource :projects do resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
# Get a project repository commits # Get a project repository commits
# #
# Parameters: # Parameters:
......
...@@ -56,8 +56,12 @@ module API ...@@ -56,8 +56,12 @@ module API
end end
end end
def paginate(object) def paginate(relation)
object.page(params[:page]).per(params[:per_page].to_i) per_page = params[:per_page].to_i
paginated = relation.page(params[:page]).per(per_page)
add_pagination_headers(paginated, per_page)
paginated
end end
def authenticate! def authenticate!
...@@ -74,6 +78,10 @@ module API ...@@ -74,6 +78,10 @@ module API
end end
end end
def authorize_push_project
authorize! :push_code, user_project
end
def authorize_admin_project def authorize_admin_project
authorize! :admin_project, user_project authorize! :admin_project, user_project
end end
...@@ -134,6 +142,18 @@ module API ...@@ -134,6 +142,18 @@ module API
private private
def add_pagination_headers(paginated, per_page)
request_url = request.url.split('?').first
links = []
links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page?
links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page?
links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first")
links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last")
header 'Link', links.join(', ')
end
def abilities def abilities
@abilities ||= begin @abilities ||= begin
abilities = Six.new abilities = Six.new
......
...@@ -15,66 +15,6 @@ module API ...@@ -15,66 +15,6 @@ module API
not_found! not_found!
end end
end end
# Get a project repository branches
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# GET /projects/:id/repository/branches
get ":id/repository/branches" do
present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project
end
# Get a single branch
#
# Parameters:
# id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# GET /projects/:id/repository/branches/:branch
get ":id/repository/branches/:branch" do
@branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
not_found!("Branch does not exist") if @branch.nil?
present @branch, with: Entities::RepoObject, project: user_project
end
# Protect a single branch
#
# Parameters:
# id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# PUT /projects/:id/repository/branches/:branch/protect
put ":id/repository/branches/:branch/protect" do
authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch])
not_found! unless @branch
protected_branch = user_project.protected_branches.find_by(name: @branch.name)
user_project.protected_branches.create(name: @branch.name) unless protected_branch
present @branch, with: Entities::RepoObject, project: user_project
end
# Unprotect a single branch
#
# Parameters:
# id (required) - The ID of a project
# branch (required) - The name of the branch
# Example Request:
# PUT /projects/:id/repository/branches/:branch/unprotect
put ":id/repository/branches/:branch/unprotect" do
authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch])
not_found! unless @branch
protected_branch = user_project.protected_branches.find_by(name: @branch.name)
protected_branch.destroy if protected_branch
present @branch, with: Entities::RepoObject, project: user_project
end
# Get a project repository tags # Get a project repository tags
# #
# Parameters: # Parameters:
...@@ -161,7 +101,7 @@ module API ...@@ -161,7 +101,7 @@ module API
repo = user_project.repository repo = user_project.repository
ref = params[:sha] ref = params[:sha]
format = params[:format] format = params[:format]
storage_path = Rails.root.join("tmp", "repositories") storage_path = Gitlab.config.gitlab.repository_downloads_path
file_path = repo.archive_repo(ref, storage_path, format) file_path = repo.archive_repo(ref, storage_path, format)
if file_path && File.exists?(file_path) if file_path && File.exists?(file_path)
......
...@@ -29,9 +29,10 @@ module Backup ...@@ -29,9 +29,10 @@ module Backup
print "Restoring MySQL database #{config['database']} ... " print "Restoring MySQL database #{config['database']} ... "
system('mysql', *mysql_args, config['database'], in: db_file_name) system('mysql', *mysql_args, config['database'], in: db_file_name)
when "postgresql" then when "postgresql" then
puts "Destructively rebuilding database schema for RAILS_ENV #{Rails.env}"
Rake::Task["db:schema:load"].invoke
print "Restoring PostgreSQL database #{config['database']} ... " print "Restoring PostgreSQL database #{config['database']} ... "
# Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE
# statements like MySQL.
Rake::Task["gitlab:db:drop_all_tables"].invoke
pg_env pg_env
system('psql', config['database'], '-f', db_file_name) system('psql', config['database'], '-f', db_file_name)
end end
......
...@@ -81,16 +81,17 @@ module Gitlab ...@@ -81,16 +81,17 @@ module Gitlab
private private
def find_by_uid(uid) def find_by_uid_and_provider
model.where(provider: provider, extern_uid: uid).last find_by_uid(uid)
end end
def username def find_by_uid(uid)
(auth.info.nickname || samaccountname).to_s.force_encoding("utf-8") # LDAP distinguished name is case-insensitive
model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last
end end
def samaccountname def username
(auth.extra[:raw_info][:samaccountname] || []).first auth.info.nickname.to_s.force_encoding("utf-8")
end end
def provider def provider
......
...@@ -149,7 +149,7 @@ exit_if_not_running(){ ...@@ -149,7 +149,7 @@ exit_if_not_running(){
} }
## Starts Unicorn and Sidekiq if they're not running. ## Starts Unicorn and Sidekiq if they're not running.
start() { start_gitlab() {
check_stale_pids check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
...@@ -167,7 +167,7 @@ start() { ...@@ -167,7 +167,7 @@ start() {
# Remove old socket if it exists # Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the web server # Start the web server
RAILS_ENV=$RAILS_ENV script/web start & RAILS_ENV=$RAILS_ENV script/web start
fi fi
# If sidekiq is already running, don't start it again. # If sidekiq is already running, don't start it again.
...@@ -184,7 +184,7 @@ start() { ...@@ -184,7 +184,7 @@ start() {
} }
## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them. ## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them.
stop() { stop_gitlab() {
exit_if_not_running exit_if_not_running
if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
...@@ -246,7 +246,7 @@ print_status() { ...@@ -246,7 +246,7 @@ print_status() {
} }
## Tells unicorn to reload it's config and Sidekiq to restart ## Tells unicorn to reload it's config and Sidekiq to restart
reload(){ reload_gitlab(){
exit_if_not_running exit_if_not_running
if [ "$wpid" = "0" ];then if [ "$wpid" = "0" ];then
echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded." echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded."
...@@ -263,12 +263,12 @@ reload(){ ...@@ -263,12 +263,12 @@ reload(){
} }
## Restarts Sidekiq and Unicorn. ## Restarts Sidekiq and Unicorn.
restart(){ restart_gitlab(){
check_status check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
stop stop_gitlab
fi fi
start start_gitlab
} }
...@@ -276,16 +276,16 @@ restart(){ ...@@ -276,16 +276,16 @@ restart(){
case "$1" in case "$1" in
start) start)
start start_gitlab
;; ;;
stop) stop)
stop stop_gitlab
;; ;;
restart) restart)
restart restart_gitlab
;; ;;
reload|force-reload) reload|force-reload)
reload reload_gitlab
;; ;;
status) status)
print_status print_status
......
...@@ -677,7 +677,20 @@ namespace :gitlab do ...@@ -677,7 +677,20 @@ namespace :gitlab do
end end
def filter def filter
Net::LDAP::Filter.present?(ldap_config.uid) uid_filter = Net::LDAP::Filter.present?(ldap_config.uid)
if user_filter
Net::LDAP::Filter.join(uid_filter, user_filter)
else
uid_filter
end
end
def user_filter
if ldap_config['user_filter'] && ldap_config.user_filter.present?
Net::LDAP::Filter.construct(ldap_config.user_filter)
else
nil
end
end end
def ldap def ldap
......
namespace :gitlab do
namespace :db do
task drop_all_tables: :environment do
connection = ActiveRecord::Base.connection
connection.tables.each do |table|
connection.drop_table(table)
end
end
end
end
...@@ -292,6 +292,20 @@ describe User do ...@@ -292,6 +292,20 @@ describe User do
end end
end end
describe 'search' do
let(:user1) { create(:user, username: 'James', email: 'james@testing.com') }
let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
it "should be case insensitive" do
User.search(user1.username.upcase).to_a.should == [user1]
User.search(user1.username.downcase).to_a.should == [user1]
User.search(user2.username.upcase).to_a.should == [user2]
User.search(user2.username.downcase).to_a.should == [user2]
User.search(user1.username.downcase).to_a.count.should == 2
User.search(user2.username.downcase).to_a.count.should == 1
end
end
describe 'by_username_or_id' do describe 'by_username_or_id' do
let(:user1) { create(:user, username: 'foo') } let(:user1) { create(:user, username: 'foo') }
......
require 'spec_helper'
require 'mime/types'
describe API::API do
include ApiHelpers
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
let!(:master) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:guest) { create(:users_project, user: user2, project: project, project_access: UsersProject::GUEST) }
describe "GET /projects/:id/repository/branches" do
it "should return an array of project branches" do
get api("/projects/#{project.id}/repository/branches", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name
end
end
describe "GET /projects/:id/repository/branches/:branch" do
it "should return the branch information for a single branch" do
get api("/projects/#{project.id}/repository/branches/new_design", user)
response.status.should == 200
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == false
end
it "should return a 403 error if guest" do
get api("/projects/#{project.id}/repository/branches", user2)
response.status.should == 403
end
it "should return a 404 error if branch is not available" do
get api("/projects/#{project.id}/repository/branches/unknown", user)
response.status.should == 404
end
end
describe "PUT /projects/:id/repository/branches/:branch/protect" do
it "should protect a single branch" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
response.status.should == 200
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == true
end
it "should return a 404 error if branch not found" do
put api("/projects/#{project.id}/repository/branches/unknown/protect", user)
response.status.should == 404
end
it "should return a 403 error if guest" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user2)
response.status.should == 403
end
it "should return success when protect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
response.status.should == 200
end
end
describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
it "should unprotect a single branch" do
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
response.status.should == 200
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == false
end
it "should return success when unprotect branch" do
put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user)
response.status.should == 404
end
it "should return success when unprotect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
response.status.should == 200
end
end
describe "POST /projects/:id/repository/branches" do
it "should create a new branch" do
post api("/projects/#{project.id}/repository/branches", user),
branch_name: 'new_design',
ref: '621491c677087aa243f165eab467bfdfbee00be1'
response.status.should == 201
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
end
it "should deny for user without push access" do
post api("/projects/#{project.id}/repository/branches", user2),
branch_name: 'new_design',
ref: '621491c677087aa243f165eab467bfdfbee00be1'
response.status.should == 403
end
end
end
...@@ -25,6 +25,12 @@ describe API::API do ...@@ -25,6 +25,12 @@ describe API::API do
json_response.should be_an Array json_response.should be_an Array
json_response.first['title'].should == issue.title json_response.first['title'].should == issue.title
end end
it "should add pagination headers" do
get api("/issues?per_page=3", user)
response.headers['Link'].should ==
'<http://www.example.com/api/v3/issues?page=1&per_page=3>; rel="first", <http://www.example.com/api/v3/issues?page=1&per_page=3>; rel="last"'
end
end end
end end
......
...@@ -14,86 +14,6 @@ describe API::API do ...@@ -14,86 +14,6 @@ describe API::API do
before { project.team << [user, :reporter] } before { project.team << [user, :reporter] }
describe "GET /projects/:id/repository/branches" do
it "should return an array of project branches" do
get api("/projects/#{project.id}/repository/branches", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name
end
end
describe "GET /projects/:id/repository/branches/:branch" do
it "should return the branch information for a single branch" do
get api("/projects/#{project.id}/repository/branches/new_design", user)
response.status.should == 200
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == false
end
it "should return a 403 error if guest" do
get api("/projects/#{project.id}/repository/branches", user2)
response.status.should == 403
end
it "should return a 404 error if branch is not available" do
get api("/projects/#{project.id}/repository/branches/unknown", user)
response.status.should == 404
end
end
describe "PUT /projects/:id/repository/branches/:branch/protect" do
it "should protect a single branch" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
response.status.should == 200
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == true
end
it "should return a 404 error if branch not found" do
put api("/projects/#{project.id}/repository/branches/unknown/protect", user)
response.status.should == 404
end
it "should return a 403 error if guest" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user2)
response.status.should == 403
end
it "should return success when protect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
response.status.should == 200
end
end
describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
it "should unprotect a single branch" do
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
response.status.should == 200
json_response['name'].should == 'new_design'
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == false
end
it "should return success when unprotect branch" do
put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user)
response.status.should == 404
end
it "should return success when unprotect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
response.status.should == 200
end
end
describe "GET /projects/:id/repository/tags" do describe "GET /projects/:id/repository/tags" do
it "should return an array of project tags" do it "should return an array of project tags" do
get api("/projects/#{project.id}/repository/tags", user) get api("/projects/#{project.id}/repository/tags", user)
......
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