Merge branch 'master' into request/relative_submodules

Conflicts:
	CHANGELOG
This commit is contained in:
Timm Drevensek 2014-04-01 22:30:16 +02:00
commit cb659e4c5a
69 changed files with 862 additions and 541 deletions

View file

@ -1,7 +1,11 @@
v 6.8.0
- 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)
- 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
- Fix upgrader script

View file

@ -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. 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. 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 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 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. 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).

View file

@ -1,7 +1,7 @@
# A sample Guardfile
# 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{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }

View file

@ -113,38 +113,10 @@ or start each component separately
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/)
* [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
All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/).
### Getting help
* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported.
* [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.
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.

View file

@ -13,6 +13,12 @@
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&:after {
content: " ";
display: table;
clear: both;
}
&.disabled {
color: #888;
}
@ -46,6 +52,12 @@
.author { color: #999; }
.list-item-name {
float: left;
position: relative;
top: 3px;
}
p {
padding-top: 1px;
margin: 0;

View file

@ -113,6 +113,5 @@
float: left;
margin-right: 3px;
color: #999;
margin-bottom: 10px;
width: 16px;
}

View file

@ -62,6 +62,29 @@
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 {
margin: 0px;
padding: 0px;
@ -125,8 +148,6 @@
}
&.parallel {
display: table-cell;
overflow: hidden;
width: 50%;
}
}
}

View file

@ -16,11 +16,7 @@ class Projects::BranchesController < Projects::ApplicationController
end
def create
@repository.add_branch(params[:branch_name], params[:ref])
if new_branch = @repository.find_branch(params[:branch_name])
Event.create_ref_event(@project, current_user, new_branch, 'add')
end
CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user)
redirect_to project_branches_path(@project)
end

View file

@ -14,7 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
render_404 and return
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)

View file

@ -105,8 +105,80 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end
def get_old_file(project, commit, diff)
project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
def parallel_diff_lines(project, commit, diff, file)
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
protected

3
app/models/diff_line.rb Normal file
View file

@ -0,0 +1,3 @@
class DiffLine
attr_accessor :type, :content, :num, :code
end

View file

@ -204,7 +204,7 @@ class User < ActiveRecord::Base
end
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
def by_username_or_id(name_or_id)

View file

@ -0,0 +1,13 @@
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

View file

@ -178,21 +178,41 @@ class NotificationService
# Get project users with WATCH notification level
def project_watchers(project)
project_watchers = []
member_methods = { project => :users_projects }
member_methods.merge!(project.group => :users_groups) if project.group
# Gather all user ids that have WATCH notification setting for project
project_notification_uids = project_notification_list(project, Notification::N_WATCH)
member_methods.each do |object, member_method|
# Get project notification settings since it has higher priority
user_ids = object.send(member_method).where(notification_level: Notification::N_WATCH).pluck(:user_id)
project_watchers += User.where(id: user_ids)
# Gather all user ids that have WATCH notification setting for group
group_notification_uids = group_notification_list(project, Notification::N_WATCH)
# next collect users who use global settings with watch state
user_ids = object.send(member_method).where(notification_level: Notification::N_GLOBAL).pluck(:user_id)
project_watchers += User.where(id: user_ids, notification_level: Notification::N_WATCH)
# Gather all user ids that have GLOBAL setting
global_notification_uids = global_notification_list(project)
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
project_watchers.uniq
def global_notification_list(project)
[
project_notification_list(project, Notification::N_GLOBAL),
group_notification_list(project, Notification::N_GLOBAL)
].flatten
end
# Remove users with disabled notifications from array

View file

@ -70,8 +70,9 @@
- @group.users_groups.order('group_access DESC').each do |member|
- user = member.user
%li{class: dom_class(user)}
%strong
= link_to user.name, admin_user_path(user)
.list-item-name
%strong
= link_to user.name, admin_user_path(user)
%span.pull-right.light
= member.human_access
= link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do

View file

@ -28,8 +28,10 @@
%ul.well-list
- @hooks.each do |hook|
%li
.list-item-name
= link_to admin_hook_path(hook) do
%strong= hook.url
.pull-right
= 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 admin_hook_path(hook) do
%strong= hook.url

View file

@ -44,9 +44,10 @@
%ul.well-list
- @projects.each do |project|
%li
%span{ class: visibility_level_color(project.visibility_level) }
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, [:admin, project]
.list-item-name
%span{ class: visibility_level_color(project.visibility_level) }
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, [:admin, project]
.pull-right
%span.label.label-gray
= repository_size(project)

View file

@ -116,8 +116,9 @@
- @project.users_projects.each do |users_project|
- user = users_project.user
%li.users_project
%strong
= link_to user.name, admin_user_path(user)
.list-item-name
%strong
= link_to user.name, admin_user_path(user)
.pull-right
- if users_project.owner?
%span.light Owner

View file

@ -36,15 +36,16 @@
%ul.well-list
- @users.each do |user|
%li
- if user.blocked?
%i.icon-lock.cred
- else
%i.icon-user.cgreen
= link_to user.name, [:admin, user]
- if user.admin?
%strong.cred (Admin)
- if user == current_user
%span.cred It's you!
.list-item-name
- if user.blocked?
%i.icon-lock.cred
- else
%i.icon-user.cgreen
= link_to user.name, [:admin, user]
- if user.admin?
%strong.cred (Admin)
- if user == current_user
%span.cred It's you!
.pull-right
%span.light
%i.icon-envelope

View file

@ -124,7 +124,8 @@
- @user.users_groups.each do |user_group|
- group = user_group.group
%li.users_group
%strong= link_to group.name, admin_group_path(group)
%span{class: ("list-item-name" unless user_group.owner?)}
%strong= link_to group.name, admin_group_path(group)
.pull-right
%span.light= user_group.human_access
- unless user_group.owner?

View file

@ -73,8 +73,9 @@
%ul.well-list
- @group.projects.each do |project|
%li
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project
.list-item-name
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project
.pull-right
= link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"

View file

@ -23,4 +23,4 @@
- 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.
- if @target_url
#{link_to "View in GitLab", @target_url}
#{link_to "View it on GitLab", @target_url}

View file

@ -7,7 +7,7 @@
%li
#{commit.short_id} - #{commit.title}
%h4 Diff:
%h4 Changes:
- @diffs.each do |diff|
%li
%strong
@ -23,6 +23,6 @@
%br
- 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?
%h5 Diff for big amount of commits is disabled
%h5 Changes are not shown due to large amount of commits

View file

@ -6,7 +6,7 @@ Commits:
#{commit.short_id} - #{truncate(commit.title, length: 40)}
\
\
Diff:
Changes:
- @diffs.each do |diff|
\
\=====================================
@ -22,4 +22,4 @@ Diff:
- if @compare.timeout
Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit?
Diff for big amount of commits is disabled
Changes are not shown due to large amount of commits

View file

@ -1,75 +1,55 @@
/ Side-by-side diff view
- old_file = get_old_file(project, @commit, diff)
- deleted_lines = {}
- 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
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length
%div.text-file-parallel
%table{ style: "table-layout: fixed;" }
- 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]
%div.diff-side.diff-side-left
%table
- old_lines.each do |line|
- 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
- if line.type == :file_created
%td.line_content.parallel= "File was created"
- elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%tr.line_holder.parallel
- if line_index == 0 && diff.new_file
%td.line_content.parallel= "File was created"
%td.old_line= ""
- elsif deleted_line
%td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line
%td.old_line.old
= line_index1 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]
- elsif old_line
%td.line_content.parallel= old_line
%td.old_line= line_index1 + 1
- else
%td.line_content.parallel= ""
%td.old_line= ""
%div.diff-middle
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line= ""
%td.diff_line=""
- if diff.deleted_file && line_index == 0
%td.new_line= ""
%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
%td.new_line= ""
%td.line_content.parallel= ""
- if new_lines[index].type == :added
%td.new_line.new= new_lines[index].num
- else
%td.new_line= new_lines[index].num
- if @reply_allowed
- comments1 = []
- comments2 = []
- 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
%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());
});
$('.diff-side-left').on('scroll', function(){
$('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
$('.diff-side-right').scrollLeft($(this).scrollLeft());
});

View file

@ -1,6 +1,6 @@
- too_big = diff.diff.lines.count > 1000
- 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}"}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|

View file

@ -18,17 +18,17 @@
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Diff
%h4 Changes
- if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger
%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
.bs-callout.bs-callout-danger
%h4 Diff for this comparison is extremely large.
%p Use command line to browse diff for this comparison.
%h4 Number of changed files for this comparison is extremely large.
%p Use command line to browse through changes for this comparison.
- else

View file

@ -2,7 +2,7 @@
Deploy key:
= @key.title
%small
created at
created on
= @key.created_at.stamp("Aug 21, 2011")
.back-link
= link_to project_deploy_keys_path(@project) do

View file

@ -1,7 +1,7 @@
%div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
%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))
.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

View file

@ -20,7 +20,7 @@
%li.diffs-tab{data: {action: 'diffs'}}
= link_to diffs_project_merge_request_path(@project, @merge_request) do
%i.icon-list-alt
Diff
Changes
- content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request)

View file

@ -5,7 +5,7 @@
- else
.bs-callout.bs-callout-warning
%h4
Diff for this comparison is extremely large.
Changes view for this comparison is extremely large.
%p
You can
= link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"

View file

@ -4,7 +4,10 @@
%strong Archived projects cannot be committed to!
- else
.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

View file

@ -2,11 +2,12 @@
- return unless user
- show_roles = true if show_roles.nil?
%li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
= image_tag avatar_icon(user.email, 16), class: "avatar s16"
%strong= user.name
%span.cgray= user.username
- if user == current_user
%span.label.label-success It's you
%span{class: ("list-item-name" if show_controls)}
= image_tag avatar_icon(user.email, 16), class: "avatar s16"
%strong= user.name
%span.cgray= user.username
- if user == current_user
%span.label.label-success It's you
- if show_roles
%span.pull-right
@ -22,7 +23,7 @@
- else
= link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white
.edit-member.hide.js-toggle-content
= form_for [@group, member], remote: true do |f|
.alert.prepend-top-20

View file

@ -76,6 +76,11 @@ production: &base
snippets: false
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
issues_tracker:
# redmine:

View file

@ -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['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['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root)
#
# Gravatar

18
doc/README.md Normal file
View file

@ -0,0 +1,18 @@
## 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)

View file

@ -1,5 +1,32 @@
# 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.
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:
+ `page` (default: `1`) - page number
+ `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
When you work with API you may notice two similar fields in api entites: id and iid.
@ -117,30 +148,3 @@ Issue
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`
## 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

198
doc/api/branches.md Normal file
View file

@ -0,0 +1,198 @@
# 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
}
```

View file

@ -1,170 +1,3 @@
## 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
Get a list of repository tags from a project, sorted by name in reverse alphabetical order.

View file

@ -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

View file

@ -0,0 +1,2 @@
+ [Architecture](architecture.md)
+ [Shell commands](shell_commands.md)

View file

@ -28,7 +28,7 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
## 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.
It uses Nginx or Apache as a web front end to proxypass the Unicorn web server.
@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production
```
Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.

4
doc/install/README.md Normal file
View file

@ -0,0 +1,4 @@
+ [Installation](installation.md)
+ [Requirements](requirements.md)
+ [Structure](structure.md)
+ [Database MySQL](database_mysql.md)

View file

@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Install the database packages
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
# 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
# change $password in the command below to a real password you pick
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
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;

2
doc/legal/README.md Normal file
View file

@ -0,0 +1,2 @@
+ [Corporate contributor license agreement](corporate_contributor_license_agreement.md)
+ [Individual contributor license agreement](individual_contributor_license_agreement.md)

View file

@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions.
|------|-----|--------|---------|------|-----|
|Browse group|✓|✓|✓|✓|✓|
|Edit group|||||✓|
|create project in group|||||✓|
|Create project in group|||||✓|
|Manage group members|||||✓|
|Remove group|||||✓|

6
doc/raketasks/README.md Normal file
View file

@ -0,0 +1,6 @@
+ [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md)
+ [Features](features.md)
+ [Maintenance](maintenance.md)
+ [User management](user_management.md)
+ [Web hooks](web_hooks.md)

2
doc/release/README.md Normal file
View file

@ -0,0 +1,2 @@
+ [Monthly](monthly.md)
+ [Security](security.md)

2
doc/security/README.md Normal file
View file

@ -0,0 +1,2 @@
+ [Password length limits](password_length_limits.md)
+ [Rack attack](rack_attack.md)

2
doc/ssh/README.md Normal file
View file

@ -0,0 +1,2 @@
+ [Deploy keys](deploy_keys.md)
+ [SSH](ssh.md)

View file

@ -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.
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.

View file

@ -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
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)

5
doc/update/README.md Normal file
View file

@ -0,0 +1,5 @@
+ [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)

View file

@ -3,24 +3,25 @@
```bash
git clone git@example.com:project-name.git
```
2. Create branch with your feature
```bash
git checkout -b $feature_name
```
3. Write code. Comit changes
3. Write code. Commit changes
```bash
git commit -am "My feature is ready"
```
4. Push your branch to GitLab
```bash
git push origin $feature_name
```
5. Review your code on Commits page
5. Review your code on commits page
6. Create a merge request
7. Your team lead will review the code &amp; merge it to the main branch

View file

@ -45,5 +45,6 @@ module API
mount Files
mount Commits
mount Namespaces
mount Branches
end
end

85
lib/api/branches.rb Normal file
View file

@ -0,0 +1,85 @@
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

View file

@ -1,21 +1,12 @@
require 'mime/types'
module API
# Projects API
# Projects commits API
class Commits < Grape::API
before { authenticate! }
before { authorize! :download_code, user_project }
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
#
# Parameters:

View file

@ -56,8 +56,12 @@ module API
end
end
def paginate(object)
object.page(params[:page]).per(params[:per_page].to_i)
def paginate(relation)
per_page = params[:per_page].to_i
paginated = relation.page(params[:page]).per(per_page)
add_pagination_headers(paginated, per_page)
paginated
end
def authenticate!
@ -74,6 +78,10 @@ module API
end
end
def authorize_push_project
authorize! :push_code, user_project
end
def authorize_admin_project
authorize! :admin_project, user_project
end
@ -134,6 +142,18 @@ module API
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
@abilities ||= begin
abilities = Six.new

View file

@ -15,66 +15,6 @@ module API
not_found!
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
#
# Parameters:
@ -161,7 +101,7 @@ module API
repo = user_project.repository
ref = params[:sha]
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)
if file_path && File.exists?(file_path)

View file

@ -29,9 +29,10 @@ module Backup
print "Restoring MySQL database #{config['database']} ... "
system('mysql', *mysql_args, config['database'], in: db_file_name)
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']} ... "
# 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
system('psql', config['database'], '-f', db_file_name)
end

View file

@ -81,16 +81,17 @@ module Gitlab
private
def find_by_uid_and_provider
find_by_uid(uid)
end
def find_by_uid(uid)
model.where(provider: provider, extern_uid: uid).last
# LDAP distinguished name is case-insensitive
model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last
end
def username
(auth.info.nickname || samaccountname).to_s.force_encoding("utf-8")
end
def samaccountname
(auth.extra[:raw_info][:samaccountname] || []).first
auth.info.nickname.to_s.force_encoding("utf-8")
end
def provider

View file

@ -149,7 +149,7 @@ exit_if_not_running(){
}
## Starts Unicorn and Sidekiq if they're not running.
start() {
start_gitlab() {
check_stale_pids
if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then
@ -167,7 +167,7 @@ start() {
# Remove old socket if it exists
rm -f "$socket_path"/gitlab.socket 2>/dev/null
# Start the web server
RAILS_ENV=$RAILS_ENV script/web start &
RAILS_ENV=$RAILS_ENV script/web start
fi
# If sidekiq is already running, don't start it again.
@ -184,7 +184,7 @@ start() {
}
## 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
if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then
@ -246,7 +246,7 @@ print_status() {
}
## Tells unicorn to reload it's config and Sidekiq to restart
reload(){
reload_gitlab(){
exit_if_not_running
if [ "$wpid" = "0" ];then
echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded."
@ -263,12 +263,12 @@ reload(){
}
## Restarts Sidekiq and Unicorn.
restart(){
restart_gitlab(){
check_status
if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then
stop
stop_gitlab
fi
start
start_gitlab
}
@ -276,16 +276,16 @@ restart(){
case "$1" in
start)
start
start_gitlab
;;
stop)
stop
stop_gitlab
;;
restart)
restart
restart_gitlab
;;
reload|force-reload)
reload
reload_gitlab
;;
status)
print_status

View file

@ -677,7 +677,20 @@ namespace :gitlab do
end
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
def ldap

View file

@ -0,0 +1,10 @@
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

View file

@ -292,6 +292,20 @@ describe User do
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
let(:user1) { create(:user, username: 'foo') }

View file

@ -0,0 +1,115 @@
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

View file

@ -25,6 +25,12 @@ describe API::API do
json_response.should be_an Array
json_response.first['title'].should == issue.title
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

View file

@ -14,86 +14,6 @@ describe API::API do
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
it "should return an array of project tags" do
get api("/projects/#{project.id}/repository/tags", user)