Commit cb659e4c5a1c32bd2aa5c9994cd9d7b4a9841c9b
Exists in
spb-stable
and in
3 other branches
Merge branch 'master' into request/relative_submodules
Conflicts: CHANGELOG
Showing
70 changed files
with
873 additions
and
552 deletions
Show diff stats
CHANGELOG
1 | v 6.8.0 | 1 | v 6.8.0 |
2 | - Ability to at mention users that are participating in issue and merge req. discussion | 2 | - Ability to at mention users that are participating in issue and merge req. discussion |
3 | - 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) | 3 | - 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) |
4 | - - Add support for relative submodules | 4 | + - Make user search case-insensitive (Christopher Arnold) |
5 | + - Remove omniauth-ldap nickname bug workaround | ||
6 | + - Drop all tables before restoring a Postgres backup | ||
7 | + - Make the repository downloads path configurable | ||
8 | + - Create branches via API (sponsored by O'Reilly Media) | ||
5 | 9 | ||
6 | v 6.7.2 | 10 | v 6.7.2 |
7 | - Fix upgrader script | 11 | - Fix upgrader script |
CONTRIBUTING.md
@@ -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 | ||
63 | 1. Add your changes to the [CHANGELOG](CHANGELOG) | 63 | 1. Add your changes to the [CHANGELOG](CHANGELOG) |
64 | 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) | 64 | 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) |
65 | 1. Push the commit to your fork | 65 | 1. Push the commit to your fork |
66 | -1. Submit a merge request (MR) | 66 | +1. Submit a merge request (MR) to the master branch |
67 | 1. The MR title should describes the change you want to make | 67 | 1. The MR title should describes the change you want to make |
68 | 1. The MR description should give a motive for your change and the method you used to achieve it | 68 | 1. The MR description should give a motive for your change and the method you used to achieve it |
69 | 1. If the MR changes the UI it should include before and after screenshots | 69 | 1. If the MR changes the UI it should include before and after screenshots |
70 | +1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R` | ||
70 | 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 | 71 | 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 |
71 | 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion | 72 | 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submittion |
72 | 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). | 73 | 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). |
Guardfile
1 | # A sample Guardfile | 1 | # A sample Guardfile |
2 | # More info at https://github.com/guard/guard#readme | 2 | # More info at https://github.com/guard/guard#readme |
3 | 3 | ||
4 | -guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do | 4 | +guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_pass: false do |
5 | watch(%r{^spec/.+_spec\.rb$}) | 5 | watch(%r{^spec/.+_spec\.rb$}) |
6 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } | 6 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } |
7 | watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" } | 7 | watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" } |
README.md
@@ -113,38 +113,10 @@ or start each component separately | @@ -113,38 +113,10 @@ or start each component separately | ||
113 | Single Spinach test: bundle exec spinach features/project/issues/milestones.feature | 113 | Single Spinach test: bundle exec spinach features/project/issues/milestones.feature |
114 | 114 | ||
115 | 115 | ||
116 | -### GitLab interfaces | 116 | +### Documentation |
117 | 117 | ||
118 | -* [GitLab API doc](doc/api/README.md) or see the [GitLab API website](http://api.gitlab.org/) | ||
119 | - | ||
120 | -* [Rake tasks](doc/raketasks) including a [backup and restore procedure](doc/raketasks/backup_restore.md) | ||
121 | - | ||
122 | -* [Directory structure](doc/install/structure.md) | ||
123 | - | ||
124 | -* [Database installation](doc/install/databases.md) | ||
125 | - | ||
126 | -* [Markdown specification](doc/markdown/markdown.md) | ||
127 | - | ||
128 | -* [Security guide](doc/security/rack_attack.md) to throttle abusive requests | 118 | +All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/). |
129 | 119 | ||
130 | ### Getting help | 120 | ### Getting help |
131 | 121 | ||
132 | -* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported. | ||
133 | - | ||
134 | -* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems. | ||
135 | - | ||
136 | -* [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. | ||
137 | - | ||
138 | -* [Feature request forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. | ||
139 | - | ||
140 | -* [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. | ||
141 | - | ||
142 | -* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions. | ||
143 | - | ||
144 | -* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations. | ||
145 | - | ||
146 | -* [#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). | ||
147 | - | ||
148 | -* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview. | ||
149 | - | ||
150 | -* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help. | 122 | +Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help. |
app/assets/stylesheets/generic/lists.scss
@@ -13,6 +13,12 @@ | @@ -13,6 +13,12 @@ | ||
13 | border-bottom: 1px solid #eee; | 13 | border-bottom: 1px solid #eee; |
14 | border-bottom: 1px solid rgba(0, 0, 0, 0.05); | 14 | border-bottom: 1px solid rgba(0, 0, 0, 0.05); |
15 | 15 | ||
16 | + &:after { | ||
17 | + content: " "; | ||
18 | + display: table; | ||
19 | + clear: both; | ||
20 | + } | ||
21 | + | ||
16 | &.disabled { | 22 | &.disabled { |
17 | color: #888; | 23 | color: #888; |
18 | } | 24 | } |
@@ -46,6 +52,12 @@ | @@ -46,6 +52,12 @@ | ||
46 | 52 | ||
47 | .author { color: #999; } | 53 | .author { color: #999; } |
48 | 54 | ||
55 | + .list-item-name { | ||
56 | + float: left; | ||
57 | + position: relative; | ||
58 | + top: 3px; | ||
59 | + } | ||
60 | + | ||
49 | p { | 61 | p { |
50 | padding-top: 1px; | 62 | padding-top: 1px; |
51 | margin: 0; | 63 | margin: 0; |
app/assets/stylesheets/sections/dashboard.scss
app/assets/stylesheets/sections/diff.scss
@@ -62,6 +62,29 @@ | @@ -62,6 +62,29 @@ | ||
62 | font-size: 12px; | 62 | font-size: 12px; |
63 | } | 63 | } |
64 | } | 64 | } |
65 | + | ||
66 | + .text-file-parallel div { | ||
67 | + display: inline-block; | ||
68 | + padding-bottom: 16px; | ||
69 | + } | ||
70 | + .diff-side { | ||
71 | + overflow-x: scroll; | ||
72 | + width: 508px; | ||
73 | + height: 700px; | ||
74 | + } | ||
75 | + .diff-side.diff-side-left{ | ||
76 | + overflow-y:hidden; | ||
77 | + } | ||
78 | + .diff-side table, td.diff-middle table { | ||
79 | + height: 700px; | ||
80 | + } | ||
81 | + .diff-middle { | ||
82 | + width: 114px; | ||
83 | + vertical-align: top; | ||
84 | + height: 700px; | ||
85 | + overflow: hidden | ||
86 | + } | ||
87 | + | ||
65 | .old_line, .new_line, .diff_line { | 88 | .old_line, .new_line, .diff_line { |
66 | margin: 0px; | 89 | margin: 0px; |
67 | padding: 0px; | 90 | padding: 0px; |
@@ -125,8 +148,6 @@ | @@ -125,8 +148,6 @@ | ||
125 | } | 148 | } |
126 | &.parallel { | 149 | &.parallel { |
127 | display: table-cell; | 150 | display: table-cell; |
128 | - overflow: hidden; | ||
129 | - width: 50%; | ||
130 | } | 151 | } |
131 | } | 152 | } |
132 | } | 153 | } |
app/controllers/projects/branches_controller.rb
@@ -16,11 +16,7 @@ class Projects::BranchesController < Projects::ApplicationController | @@ -16,11 +16,7 @@ class Projects::BranchesController < Projects::ApplicationController | ||
16 | end | 16 | end |
17 | 17 | ||
18 | def create | 18 | def create |
19 | - @repository.add_branch(params[:branch_name], params[:ref]) | ||
20 | - | ||
21 | - if new_branch = @repository.find_branch(params[:branch_name]) | ||
22 | - Event.create_ref_event(@project, current_user, new_branch, 'add') | ||
23 | - end | 19 | + CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user) |
24 | 20 | ||
25 | redirect_to project_branches_path(@project) | 21 | redirect_to project_branches_path(@project) |
26 | end | 22 | end |
app/controllers/projects/repositories_controller.rb
@@ -14,7 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController | @@ -14,7 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController | ||
14 | render_404 and return | 14 | render_404 and return |
15 | end | 15 | end |
16 | 16 | ||
17 | - storage_path = Rails.root.join("tmp", "repositories") | 17 | + storage_path = Gitlab.config.gitlab.repository_downloads_path |
18 | 18 | ||
19 | file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase) | 19 | file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase) |
20 | 20 |
app/helpers/commits_helper.rb
@@ -105,8 +105,80 @@ module CommitsHelper | @@ -105,8 +105,80 @@ module CommitsHelper | ||
105 | branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe | 105 | branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe |
106 | end | 106 | end |
107 | 107 | ||
108 | - def get_old_file(project, commit, diff) | ||
109 | - project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id | 108 | + def parallel_diff_lines(project, commit, diff, file) |
109 | + old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id | ||
110 | + deleted_lines = {} | ||
111 | + added_lines = {} | ||
112 | + each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old| | ||
113 | + if type == "old" | ||
114 | + deleted_lines[line_old] = { line_code: line_code, type: type, line: line } | ||
115 | + elsif type == "new" | ||
116 | + added_lines[line_new] = { line_code: line_code, type: type, line: line } | ||
117 | + end | ||
118 | + end | ||
119 | + max_length = old_file ? old_file.sloc + added_lines.length : file.sloc | ||
120 | + | ||
121 | + offset1 = 0 | ||
122 | + offset2 = 0 | ||
123 | + old_lines = [] | ||
124 | + new_lines = [] | ||
125 | + | ||
126 | + max_length.times do |line_index| | ||
127 | + line_index1 = line_index - offset1 | ||
128 | + line_index2 = line_index - offset2 | ||
129 | + deleted_line = deleted_lines[line_index1 + 1] | ||
130 | + added_line = added_lines[line_index2 + 1] | ||
131 | + old_line = old_file.lines[line_index1] if old_file | ||
132 | + new_line = file.lines[line_index2] | ||
133 | + | ||
134 | + if deleted_line && added_line | ||
135 | + elsif deleted_line | ||
136 | + new_line = nil | ||
137 | + offset2 += 1 | ||
138 | + elsif added_line | ||
139 | + old_line = nil | ||
140 | + offset1 += 1 | ||
141 | + end | ||
142 | + | ||
143 | + old_lines[line_index] = DiffLine.new | ||
144 | + new_lines[line_index] = DiffLine.new | ||
145 | + | ||
146 | + # old | ||
147 | + if line_index == 0 && diff.new_file | ||
148 | + old_lines[line_index].type = :file_created | ||
149 | + old_lines[line_index].content = 'File was created' | ||
150 | + elsif deleted_line | ||
151 | + old_lines[line_index].type = :deleted | ||
152 | + old_lines[line_index].content = old_line | ||
153 | + old_lines[line_index].num = line_index1 + 1 | ||
154 | + old_lines[line_index].code = deleted_line[:line_code] | ||
155 | + elsif old_line | ||
156 | + old_lines[line_index].type = :no_change | ||
157 | + old_lines[line_index].content = old_line | ||
158 | + old_lines[line_index].num = line_index1 + 1 | ||
159 | + else | ||
160 | + old_lines[line_index].type = :added | ||
161 | + end | ||
162 | + | ||
163 | + # new | ||
164 | + if line_index == 0 && diff.deleted_file | ||
165 | + new_lines[line_index].type = :file_deleted | ||
166 | + new_lines[line_index].content = "File was deleted" | ||
167 | + elsif added_line | ||
168 | + new_lines[line_index].type = :added | ||
169 | + new_lines[line_index].num = line_index2 + 1 | ||
170 | + new_lines[line_index].content = new_line | ||
171 | + new_lines[line_index].code = added_line[:line_code] | ||
172 | + elsif new_line | ||
173 | + new_lines[line_index].type = :no_change | ||
174 | + new_lines[line_index].num = line_index2 + 1 | ||
175 | + new_lines[line_index].content = new_line | ||
176 | + else | ||
177 | + new_lines[line_index].type = :deleted | ||
178 | + end | ||
179 | + end | ||
180 | + | ||
181 | + return old_lines, new_lines | ||
110 | end | 182 | end |
111 | 183 | ||
112 | protected | 184 | protected |
app/models/user.rb
@@ -204,7 +204,7 @@ class User < ActiveRecord::Base | @@ -204,7 +204,7 @@ class User < ActiveRecord::Base | ||
204 | end | 204 | end |
205 | 205 | ||
206 | def search query | 206 | def search query |
207 | - where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") | 207 | + where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%") |
208 | end | 208 | end |
209 | 209 | ||
210 | def by_username_or_id(name_or_id) | 210 | def by_username_or_id(name_or_id) |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +class CreateBranchService | ||
2 | + def execute(project, branch_name, ref, current_user) | ||
3 | + repository = project.repository | ||
4 | + repository.add_branch(branch_name, ref) | ||
5 | + new_branch = repository.find_branch(branch_name) | ||
6 | + | ||
7 | + if new_branch | ||
8 | + Event.create_ref_event(project, current_user, new_branch, 'add') | ||
9 | + end | ||
10 | + | ||
11 | + new_branch | ||
12 | + end | ||
13 | +end |
app/services/notification_service.rb
@@ -178,21 +178,41 @@ class NotificationService | @@ -178,21 +178,41 @@ class NotificationService | ||
178 | 178 | ||
179 | # Get project users with WATCH notification level | 179 | # Get project users with WATCH notification level |
180 | def project_watchers(project) | 180 | def project_watchers(project) |
181 | - project_watchers = [] | ||
182 | - member_methods = { project => :users_projects } | ||
183 | - member_methods.merge!(project.group => :users_groups) if project.group | ||
184 | - | ||
185 | - member_methods.each do |object, member_method| | ||
186 | - # Get project notification settings since it has higher priority | ||
187 | - user_ids = object.send(member_method).where(notification_level: Notification::N_WATCH).pluck(:user_id) | ||
188 | - project_watchers += User.where(id: user_ids) | ||
189 | - | ||
190 | - # next collect users who use global settings with watch state | ||
191 | - user_ids = object.send(member_method).where(notification_level: Notification::N_GLOBAL).pluck(:user_id) | ||
192 | - project_watchers += User.where(id: user_ids, notification_level: Notification::N_WATCH) | 181 | + # Gather all user ids that have WATCH notification setting for project |
182 | + project_notification_uids = project_notification_list(project, Notification::N_WATCH) | ||
183 | + | ||
184 | + # Gather all user ids that have WATCH notification setting for group | ||
185 | + group_notification_uids = group_notification_list(project, Notification::N_WATCH) | ||
186 | + | ||
187 | + # Gather all user ids that have GLOBAL setting | ||
188 | + global_notification_uids = global_notification_list(project) | ||
189 | + | ||
190 | + project_and_group_uids = [project_notification_uids, group_notification_uids].flatten.uniq | ||
191 | + group_and_project_watchers = User.where(id: project_and_group_uids) | ||
192 | + | ||
193 | + # Find all users that have WATCH as their GLOBAL setting | ||
194 | + global_watchers = User.where(id: global_notification_uids, notification_level: Notification::N_WATCH) | ||
195 | + | ||
196 | + [group_and_project_watchers, global_watchers].flatten.uniq | ||
197 | + end | ||
198 | + | ||
199 | + def project_notification_list(project, notification_level) | ||
200 | + project.users_projects.where(notification_level: notification_level).pluck(:user_id) | ||
201 | + end | ||
202 | + | ||
203 | + def group_notification_list(project, notification_level) | ||
204 | + if project.group | ||
205 | + project.group.users_groups.where(notification_level: notification_level).pluck(:user_id) | ||
206 | + else | ||
207 | + [] | ||
193 | end | 208 | end |
209 | + end | ||
194 | 210 | ||
195 | - project_watchers.uniq | 211 | + def global_notification_list(project) |
212 | + [ | ||
213 | + project_notification_list(project, Notification::N_GLOBAL), | ||
214 | + group_notification_list(project, Notification::N_GLOBAL) | ||
215 | + ].flatten | ||
196 | end | 216 | end |
197 | 217 | ||
198 | # Remove users with disabled notifications from array | 218 | # Remove users with disabled notifications from array |
app/views/admin/groups/show.html.haml
@@ -70,8 +70,9 @@ | @@ -70,8 +70,9 @@ | ||
70 | - @group.users_groups.order('group_access DESC').each do |member| | 70 | - @group.users_groups.order('group_access DESC').each do |member| |
71 | - user = member.user | 71 | - user = member.user |
72 | %li{class: dom_class(user)} | 72 | %li{class: dom_class(user)} |
73 | - %strong | ||
74 | - = link_to user.name, admin_user_path(user) | 73 | + .list-item-name |
74 | + %strong | ||
75 | + = link_to user.name, admin_user_path(user) | ||
75 | %span.pull-right.light | 76 | %span.pull-right.light |
76 | = member.human_access | 77 | = member.human_access |
77 | = 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 | 78 | = 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 |
app/views/admin/hooks/index.html.haml
@@ -28,8 +28,10 @@ | @@ -28,8 +28,10 @@ | ||
28 | %ul.well-list | 28 | %ul.well-list |
29 | - @hooks.each do |hook| | 29 | - @hooks.each do |hook| |
30 | %li | 30 | %li |
31 | + .list-item-name | ||
32 | + = link_to admin_hook_path(hook) do | ||
33 | + %strong= hook.url | ||
34 | + | ||
31 | .pull-right | 35 | .pull-right |
32 | = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small" | 36 | = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small" |
33 | = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-small" | 37 | = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-small" |
34 | - = link_to admin_hook_path(hook) do | ||
35 | - %strong= hook.url |
app/views/admin/projects/index.html.haml
@@ -44,9 +44,10 @@ | @@ -44,9 +44,10 @@ | ||
44 | %ul.well-list | 44 | %ul.well-list |
45 | - @projects.each do |project| | 45 | - @projects.each do |project| |
46 | %li | 46 | %li |
47 | - %span{ class: visibility_level_color(project.visibility_level) } | ||
48 | - = visibility_level_icon(project.visibility_level) | ||
49 | - = link_to project.name_with_namespace, [:admin, project] | 47 | + .list-item-name |
48 | + %span{ class: visibility_level_color(project.visibility_level) } | ||
49 | + = visibility_level_icon(project.visibility_level) | ||
50 | + = link_to project.name_with_namespace, [:admin, project] | ||
50 | .pull-right | 51 | .pull-right |
51 | %span.label.label-gray | 52 | %span.label.label-gray |
52 | = repository_size(project) | 53 | = repository_size(project) |
app/views/admin/projects/show.html.haml
@@ -116,8 +116,9 @@ | @@ -116,8 +116,9 @@ | ||
116 | - @project.users_projects.each do |users_project| | 116 | - @project.users_projects.each do |users_project| |
117 | - user = users_project.user | 117 | - user = users_project.user |
118 | %li.users_project | 118 | %li.users_project |
119 | - %strong | ||
120 | - = link_to user.name, admin_user_path(user) | 119 | + .list-item-name |
120 | + %strong | ||
121 | + = link_to user.name, admin_user_path(user) | ||
121 | .pull-right | 122 | .pull-right |
122 | - if users_project.owner? | 123 | - if users_project.owner? |
123 | %span.light Owner | 124 | %span.light Owner |
app/views/admin/users/index.html.haml
@@ -36,15 +36,16 @@ | @@ -36,15 +36,16 @@ | ||
36 | %ul.well-list | 36 | %ul.well-list |
37 | - @users.each do |user| | 37 | - @users.each do |user| |
38 | %li | 38 | %li |
39 | - - if user.blocked? | ||
40 | - %i.icon-lock.cred | ||
41 | - - else | ||
42 | - %i.icon-user.cgreen | ||
43 | - = link_to user.name, [:admin, user] | ||
44 | - - if user.admin? | ||
45 | - %strong.cred (Admin) | ||
46 | - - if user == current_user | ||
47 | - %span.cred It's you! | 39 | + .list-item-name |
40 | + - if user.blocked? | ||
41 | + %i.icon-lock.cred | ||
42 | + - else | ||
43 | + %i.icon-user.cgreen | ||
44 | + = link_to user.name, [:admin, user] | ||
45 | + - if user.admin? | ||
46 | + %strong.cred (Admin) | ||
47 | + - if user == current_user | ||
48 | + %span.cred It's you! | ||
48 | .pull-right | 49 | .pull-right |
49 | %span.light | 50 | %span.light |
50 | %i.icon-envelope | 51 | %i.icon-envelope |
app/views/admin/users/show.html.haml
@@ -124,7 +124,8 @@ | @@ -124,7 +124,8 @@ | ||
124 | - @user.users_groups.each do |user_group| | 124 | - @user.users_groups.each do |user_group| |
125 | - group = user_group.group | 125 | - group = user_group.group |
126 | %li.users_group | 126 | %li.users_group |
127 | - %strong= link_to group.name, admin_group_path(group) | 127 | + %span{class: ("list-item-name" unless user_group.owner?)} |
128 | + %strong= link_to group.name, admin_group_path(group) | ||
128 | .pull-right | 129 | .pull-right |
129 | %span.light= user_group.human_access | 130 | %span.light= user_group.human_access |
130 | - unless user_group.owner? | 131 | - unless user_group.owner? |
app/views/groups/edit.html.haml
@@ -73,8 +73,9 @@ | @@ -73,8 +73,9 @@ | ||
73 | %ul.well-list | 73 | %ul.well-list |
74 | - @group.projects.each do |project| | 74 | - @group.projects.each do |project| |
75 | %li | 75 | %li |
76 | - = visibility_level_icon(project.visibility_level) | ||
77 | - = link_to project.name_with_namespace, project | 76 | + .list-item-name |
77 | + = visibility_level_icon(project.visibility_level) | ||
78 | + = link_to project.name_with_namespace, project | ||
78 | .pull-right | 79 | .pull-right |
79 | = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" | 80 | = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" |
80 | = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" | 81 | = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" |
app/views/layouts/notify.html.haml
@@ -23,4 +23,4 @@ | @@ -23,4 +23,4 @@ | ||
23 | - if @project | 23 | - if @project |
24 | You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team. | 24 | You're receiving this notification because you are a member of the #{link_to @project.name_with_namespace, project_url(@project)} project team. |
25 | - if @target_url | 25 | - if @target_url |
26 | - #{link_to "View in GitLab", @target_url} | 26 | + #{link_to "View it on GitLab", @target_url} |
app/views/notify/repository_push_email.html.haml
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | %li | 7 | %li |
8 | #{commit.short_id} - #{commit.title} | 8 | #{commit.short_id} - #{commit.title} |
9 | 9 | ||
10 | -%h4 Diff: | 10 | +%h4 Changes: |
11 | - @diffs.each do |diff| | 11 | - @diffs.each do |diff| |
12 | %li | 12 | %li |
13 | %strong | 13 | %strong |
@@ -23,6 +23,6 @@ | @@ -23,6 +23,6 @@ | ||
23 | %br | 23 | %br |
24 | 24 | ||
25 | - if @compare.timeout | 25 | - if @compare.timeout |
26 | - %h5 Huge diff. To prevent performance issues it was hidden | 26 | + %h5 To prevent performance issues changes are hidden |
27 | - elsif @compare.commits_over_limit? | 27 | - elsif @compare.commits_over_limit? |
28 | - %h5 Diff for big amount of commits is disabled | 28 | + %h5 Changes are not shown due to large amount of commits |
app/views/notify/repository_push_email.text.haml
@@ -6,7 +6,7 @@ Commits: | @@ -6,7 +6,7 @@ Commits: | ||
6 | #{commit.short_id} - #{truncate(commit.title, length: 40)} | 6 | #{commit.short_id} - #{truncate(commit.title, length: 40)} |
7 | \ | 7 | \ |
8 | \ | 8 | \ |
9 | -Diff: | 9 | +Changes: |
10 | - @diffs.each do |diff| | 10 | - @diffs.each do |diff| |
11 | \ | 11 | \ |
12 | \===================================== | 12 | \===================================== |
@@ -22,4 +22,4 @@ Diff: | @@ -22,4 +22,4 @@ Diff: | ||
22 | - if @compare.timeout | 22 | - if @compare.timeout |
23 | Huge diff. To prevent performance issues it was hidden | 23 | Huge diff. To prevent performance issues it was hidden |
24 | - elsif @compare.commits_over_limit? | 24 | - elsif @compare.commits_over_limit? |
25 | - Diff for big amount of commits is disabled | 25 | + Changes are not shown due to large amount of commits |
app/views/projects/commits/_parallel_view.html.haml
1 | / Side-by-side diff view | 1 | / Side-by-side diff view |
2 | -- old_file = get_old_file(project, @commit, diff) | ||
3 | -- deleted_lines = {} | ||
4 | -- added_lines = {} | ||
5 | -- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| | ||
6 | - - if type == "old" | ||
7 | - - deleted_lines[line_old] = { line_code: line_code, type: type, line: line } | ||
8 | - - elsif type == "new" | ||
9 | - - added_lines[line_new] = { line_code: line_code, type: type, line: line } | ||
10 | - | ||
11 | -- max_length = old_file.sloc + added_lines.length if old_file | ||
12 | -- max_length ||= file.sloc | ||
13 | -- offset1 = 0 | ||
14 | -- offset2 = 0 | 2 | +- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) |
3 | +- num_lines = old_lines.length | ||
15 | 4 | ||
16 | %div.text-file-parallel | 5 | %div.text-file-parallel |
17 | - %table{ style: "table-layout: fixed;" } | ||
18 | - - max_length.times do |line_index| | ||
19 | - - line_index1 = line_index - offset1 | ||
20 | - - line_index2 = line_index - offset2 | ||
21 | - - deleted_line = deleted_lines[line_index1 + 1] | ||
22 | - - added_line = added_lines[line_index2 + 1] | ||
23 | - - old_line = old_file.lines[line_index1] if old_file | ||
24 | - - new_line = file.lines[line_index2] | 6 | + %div.diff-side.diff-side-left |
7 | + %table | ||
8 | + - old_lines.each do |line| | ||
9 | + | ||
10 | + %tr.line_holder.parallel | ||
11 | + - if line.type == :file_created | ||
12 | + %td.line_content.parallel= "File was created" | ||
13 | + - elsif line.type == :deleted | ||
14 | + %td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content | ||
15 | + - else line.type == :no_change | ||
16 | + %td.line_content.parallel= line.content | ||
17 | + | ||
18 | + %div.diff-middle | ||
19 | + %table | ||
20 | + - num_lines.times do |index| | ||
21 | + %tr | ||
22 | + - if old_lines[index].type == :deleted | ||
23 | + %td.old_line.old= old_lines[index].num | ||
24 | + - else | ||
25 | + %td.old_line= old_lines[index].num | ||
26 | + | ||
27 | + %td.diff_line="" | ||
25 | 28 | ||
26 | - - if deleted_line && added_line | ||
27 | - - elsif deleted_line | ||
28 | - - new_line = nil | ||
29 | - - offset2 += 1 | ||
30 | - - elsif added_line | ||
31 | - - old_line = nil | ||
32 | - - offset1 += 1 | 29 | + - if new_lines[index].type == :added |
30 | + %td.new_line.new= new_lines[index].num | ||
31 | + - else | ||
32 | + %td.new_line= new_lines[index].num | ||
33 | 33 | ||
34 | - %tr.line_holder.parallel | ||
35 | - - if line_index == 0 && diff.new_file | ||
36 | - %td.line_content.parallel= "File was created" | ||
37 | - %td.old_line= "" | ||
38 | - - elsif deleted_line | ||
39 | - %td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line | ||
40 | - %td.old_line.old | ||
41 | - = line_index1 + 1 | ||
42 | - - if @comments_allowed | ||
43 | - =# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code] | ||
44 | - - elsif old_line | ||
45 | - %td.line_content.parallel= old_line | ||
46 | - %td.old_line= line_index1 + 1 | ||
47 | - - else | ||
48 | - %td.line_content.parallel= "" | ||
49 | - %td.old_line= "" | 34 | + %div.diff-side.diff-side-right |
35 | + %table | ||
36 | + - new_lines.each do |line| | ||
50 | 37 | ||
51 | - %td.diff_line= "" | 38 | + %tr.line_holder.parallel |
39 | + - if line.type == :file_deleted | ||
40 | + %td.line_content.parallel= "File was deleted" | ||
41 | + - elsif line.type == :added | ||
42 | + %td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content | ||
43 | + - else line.type == :no_change | ||
44 | + %td.line_content.parallel= line.content | ||
52 | 45 | ||
53 | - - if diff.deleted_file && line_index == 0 | ||
54 | - %td.new_line= "" | ||
55 | - %td.line_content.parallel= "File was deleted" | ||
56 | - - elsif added_line | ||
57 | - %td.new_line.new | ||
58 | - = line_index2 + 1 | ||
59 | - - if @comments_allowed | ||
60 | - =# render "projects/notes/diff_note_link", line_code: added_line[:line_code] | ||
61 | - %td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line | ||
62 | - - elsif new_line | ||
63 | - %td.new_line= line_index2 + 1 | ||
64 | - %td.line_content.parallel= new_line | ||
65 | - - else | ||
66 | - %td.new_line= "" | ||
67 | - %td.line_content.parallel= "" | 46 | +:javascript |
47 | + $('.diff-side-right').on('scroll', function(){ | ||
48 | + $('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop()); | ||
49 | + $('.diff-side-left').scrollLeft($(this).scrollLeft()); | ||
50 | + }); | ||
68 | 51 | ||
69 | - - if @reply_allowed | ||
70 | - - comments1 = [] | ||
71 | - - comments2 = [] | ||
72 | - - comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line | ||
73 | - - comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line | ||
74 | - - unless comments1.empty? && comments2.empty? | ||
75 | - = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line | ||
76 | \ No newline at end of file | 52 | \ No newline at end of file |
53 | + $('.diff-side-left').on('scroll', function(){ | ||
54 | + $('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant | ||
55 | + $('.diff-side-right').scrollLeft($(this).scrollLeft()); | ||
56 | + }); |
app/views/projects/commits/_text_file.html.haml
1 | - too_big = diff.diff.lines.count > 1000 | 1 | - too_big = diff.diff.lines.count > 1000 |
2 | - if too_big | 2 | - if too_big |
3 | - %a.supp_diff_link Diff suppressed. Click to show | 3 | + %a.supp_diff_link Changes suppressed. Click to show |
4 | 4 | ||
5 | %table.text-file{class: "#{'hide' if too_big}"} | 5 | %table.text-file{class: "#{'hide' if too_big}"} |
6 | - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| | 6 | - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| |
app/views/projects/compare/show.html.haml
@@ -18,17 +18,17 @@ | @@ -18,17 +18,17 @@ | ||
18 | - else | 18 | - else |
19 | %ul.well-list= render Commit.decorate(@commits), project: @project | 19 | %ul.well-list= render Commit.decorate(@commits), project: @project |
20 | 20 | ||
21 | - %h4 Diff | 21 | + %h4 Changes |
22 | - if @diffs.present? | 22 | - if @diffs.present? |
23 | = render "projects/commits/diffs", diffs: @diffs, project: @project | 23 | = render "projects/commits/diffs", diffs: @diffs, project: @project |
24 | - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE | 24 | - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE |
25 | .bs-callout.bs-callout-danger | 25 | .bs-callout.bs-callout-danger |
26 | %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. | 26 | %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. |
27 | - %p To preserve performance the line diff is not shown. | 27 | + %p To preserve performance the line changes are not shown. |
28 | - elsif @timeout | 28 | - elsif @timeout |
29 | .bs-callout.bs-callout-danger | 29 | .bs-callout.bs-callout-danger |
30 | - %h4 Diff for this comparison is extremely large. | ||
31 | - %p Use command line to browse diff for this comparison. | 30 | + %h4 Number of changed files for this comparison is extremely large. |
31 | + %p Use command line to browse through changes for this comparison. | ||
32 | 32 | ||
33 | 33 | ||
34 | - else | 34 | - else |
app/views/projects/deploy_keys/show.html.haml
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | Deploy key: | 2 | Deploy key: |
3 | = @key.title | 3 | = @key.title |
4 | %small | 4 | %small |
5 | - created at | 5 | + created on |
6 | = @key.created_at.stamp("Aug 21, 2011") | 6 | = @key.created_at.stamp("Aug 21, 2011") |
7 | .back-link | 7 | .back-link |
8 | = link_to project_deploy_keys_path(@project) do | 8 | = link_to project_deploy_keys_path(@project) do |
app/views/projects/issues/_form.html.haml
1 | %div.issue-form-holder | 1 | %div.issue-form-holder |
2 | %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" | 2 | %h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}" |
3 | %hr | 3 | %hr |
4 | - - if @repository.contribution_guide && !@issue.persisted? | 4 | + - if !@repository.empty? && @repository.contribution_guide && !@issue.persisted? |
5 | - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) | 5 | - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name)) |
6 | .alert.alert-info.col-sm-10.col-sm-offset-2 | 6 | .alert.alert-info.col-sm-10.col-sm-offset-2 |
7 | ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe | 7 | ="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe |
app/views/projects/merge_requests/_show.html.haml
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | %li.diffs-tab{data: {action: 'diffs'}} | 20 | %li.diffs-tab{data: {action: 'diffs'}} |
21 | = link_to diffs_project_merge_request_path(@project, @merge_request) do | 21 | = link_to diffs_project_merge_request_path(@project, @merge_request) do |
22 | %i.icon-list-alt | 22 | %i.icon-list-alt |
23 | - Diff | 23 | + Changes |
24 | 24 | ||
25 | - content_for :note_actions do | 25 | - content_for :note_actions do |
26 | - if can?(current_user, :modify_merge_request, @merge_request) | 26 | - if can?(current_user, :modify_merge_request, @merge_request) |
app/views/projects/merge_requests/show/_diffs.html.haml
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | - else | 5 | - else |
6 | .bs-callout.bs-callout-warning | 6 | .bs-callout.bs-callout-warning |
7 | %h4 | 7 | %h4 |
8 | - Diff for this comparison is extremely large. | 8 | + Changes view for this comparison is extremely large. |
9 | %p | 9 | %p |
10 | You can | 10 | You can |
11 | = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink" | 11 | = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink" |
app/views/projects/merge_requests/show/_mr_accept.html.haml
@@ -4,7 +4,10 @@ | @@ -4,7 +4,10 @@ | ||
4 | %strong Archived projects cannot be committed to! | 4 | %strong Archived projects cannot be committed to! |
5 | - else | 5 | - else |
6 | .bs-callout | 6 | .bs-callout |
7 | - %strong You don't have permission to merge this MR | 7 | + .automerge_widget.cannot_be_merged.hide |
8 | + %strong This can't be merged automatically, even if it could be merged you don't have the permission to do so. | ||
9 | + .automerge_widget.can_be_merged.hide | ||
10 | + %strong This can be merged automatically but you don't have the permission to do so. | ||
8 | 11 | ||
9 | 12 | ||
10 | - if @show_merge_controls | 13 | - if @show_merge_controls |
app/views/users_groups/_users_group.html.haml
@@ -2,11 +2,12 @@ | @@ -2,11 +2,12 @@ | ||
2 | - return unless user | 2 | - return unless user |
3 | - show_roles = true if show_roles.nil? | 3 | - show_roles = true if show_roles.nil? |
4 | %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} | 4 | %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} |
5 | - = image_tag avatar_icon(user.email, 16), class: "avatar s16" | ||
6 | - %strong= user.name | ||
7 | - %span.cgray= user.username | ||
8 | - - if user == current_user | ||
9 | - %span.label.label-success It's you | 5 | + %span{class: ("list-item-name" if show_controls)} |
6 | + = image_tag avatar_icon(user.email, 16), class: "avatar s16" | ||
7 | + %strong= user.name | ||
8 | + %span.cgray= user.username | ||
9 | + - if user == current_user | ||
10 | + %span.label.label-success It's you | ||
10 | 11 | ||
11 | - if show_roles | 12 | - if show_roles |
12 | %span.pull-right | 13 | %span.pull-right |
@@ -22,7 +23,7 @@ | @@ -22,7 +23,7 @@ | ||
22 | - else | 23 | - else |
23 | = 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 | 24 | = 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 |
24 | %i.icon-minus.icon-white | 25 | %i.icon-minus.icon-white |
25 | - | 26 | + |
26 | .edit-member.hide.js-toggle-content | 27 | .edit-member.hide.js-toggle-content |
27 | = form_for [@group, member], remote: true do |f| | 28 | = form_for [@group, member], remote: true do |f| |
28 | .alert.prepend-top-20 | 29 | .alert.prepend-top-20 |
config/gitlab.yml.example
@@ -76,6 +76,11 @@ production: &base | @@ -76,6 +76,11 @@ production: &base | ||
76 | snippets: false | 76 | snippets: false |
77 | visibility_level: "private" # can be "private" | "internal" | "public" | 77 | visibility_level: "private" # can be "private" | "internal" | "public" |
78 | 78 | ||
79 | + ## Repository downloads directory | ||
80 | + # When a user clicks e.g. 'Download zip' on a project, a temporary zip file is created in the following directory. | ||
81 | + # The default is 'tmp/repositories' relative to the root of the Rails app. | ||
82 | + # repository_downloads_path: tmp/repositories | ||
83 | + | ||
79 | ## External issues trackers | 84 | ## External issues trackers |
80 | issues_tracker: | 85 | issues_tracker: |
81 | # redmine: | 86 | # redmine: |
config/initializers/1_settings.rb
@@ -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 | ||
97 | Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? | 97 | Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? |
98 | Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? | 98 | Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? |
99 | Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) | 99 | Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) |
100 | +Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root) | ||
100 | 101 | ||
101 | # | 102 | # |
102 | # Gravatar | 103 | # Gravatar |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +## The GitLab Documentation covers the following subjects | ||
2 | + | ||
3 | ++ [API](api/README.md) | ||
4 | ++ [Development](development/README.md) | ||
5 | ++ [Install](install/README.md) | ||
6 | ++ [Integration](integration/external-issue-tracker.md) | ||
7 | ++ [Legal](legal/README.md) | ||
8 | ++ [Markdown](markdown/markdown.md) | ||
9 | ++ [Permissions](permissions/permissions.md) | ||
10 | ++ [Public access](public_access/public_access.md) | ||
11 | ++ [Raketasks](raketasks/README.md) | ||
12 | ++ [Release](release/README.md) | ||
13 | ++ [Security](security/README.md) | ||
14 | ++ [SSH](ssh/README.md) | ||
15 | ++ [System hooks](system_hooks/system_hooks.md) | ||
16 | ++ [Update](update/README.md) | ||
17 | ++ [Web hooks](web_hooks/web_hooks.md) | ||
18 | ++ [Workflow](workflow/workflow.md) |
doc/api/README.md
1 | # GitLab API | 1 | # GitLab API |
2 | 2 | ||
3 | +## End-points | ||
4 | + | ||
5 | ++ [Users](users.md) | ||
6 | ++ [Session](session.md) | ||
7 | ++ [Projects](projects.md) | ||
8 | ++ [Project Snippets](project_snippets.md) | ||
9 | ++ [Repositories](repositories.md) | ||
10 | ++ [Repository Files](repository_files.md) | ||
11 | ++ [Commits](commits.md) | ||
12 | ++ [Branches](branches.md) | ||
13 | ++ [Merge Requests](merge_requests.md) | ||
14 | ++ [Issues](issues.md) | ||
15 | ++ [Milestones](milestones.md) | ||
16 | ++ [Notes](notes.md) | ||
17 | ++ [Deploy Keys](deploy_keys.md) | ||
18 | ++ [System Hooks](system_hooks.md) | ||
19 | ++ [Groups](groups.md) | ||
20 | + | ||
21 | +## Clients | ||
22 | + | ||
23 | ++ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP | ||
24 | ++ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby | ||
25 | ++ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python | ||
26 | ++ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java | ||
27 | + | ||
28 | +## Introduction | ||
29 | + | ||
3 | 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. | 30 | 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. |
4 | 31 | ||
5 | If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: | 32 | 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: | ||
103 | + `page` (default: `1`) - page number | 130 | + `page` (default: `1`) - page number |
104 | + `per_page` (default: `20`, max: `100`) - number of items to list per page | 131 | + `per_page` (default: `20`, max: `100`) - number of items to list per page |
105 | 132 | ||
133 | +[Link headers](http://www.w3.org/wiki/LinkHeader) are send back with each response. | ||
134 | +These have `rel` prev/next/first/last and contain the relevant url. | ||
135 | +Please use these instead of generating your own urls. | ||
136 | + | ||
106 | ## id vs iid | 137 | ## id vs iid |
107 | 138 | ||
108 | When you work with API you may notice two similar fields in api entites: id and iid. | 139 | 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 | ||
117 | 148 | ||
118 | So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json` | 149 | So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json` |
119 | But when you want to create a link to web page - use `http:://host/project/issues/:iid.json` | 150 | But when you want to create a link to web page - use `http:://host/project/issues/:iid.json` |
120 | - | ||
121 | - | ||
122 | - | ||
123 | -## Contents | ||
124 | - | ||
125 | -+ [Users](users.md) | ||
126 | -+ [Session](session.md) | ||
127 | -+ [Projects](projects.md) | ||
128 | -+ [Project Snippets](project_snippets.md) | ||
129 | -+ [Repositories](repositories.md) | ||
130 | -+ [Repository Files](repository_files.md) | ||
131 | -+ [Commits](commits.md) | ||
132 | -+ [Merge Requests](merge_requests.md) | ||
133 | -+ [Issues](issues.md) | ||
134 | -+ [Milestones](milestones.md) | ||
135 | -+ [Notes](notes.md) | ||
136 | -+ [Deploy Keys](deploy_keys.md) | ||
137 | -+ [System Hooks](system_hooks.md) | ||
138 | -+ [Groups](groups.md) | ||
139 | - | ||
140 | - | ||
141 | -## Clients | ||
142 | - | ||
143 | -+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP | ||
144 | -+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby | ||
145 | -+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python | ||
146 | -+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java |
@@ -0,0 +1,198 @@ | @@ -0,0 +1,198 @@ | ||
1 | +# Branches | ||
2 | + | ||
3 | +## List repository branches | ||
4 | + | ||
5 | +Get a list of repository branches from a project, sorted by name alphabetically. | ||
6 | + | ||
7 | +``` | ||
8 | +GET /projects/:id/repository/branches | ||
9 | +``` | ||
10 | + | ||
11 | +Parameters: | ||
12 | + | ||
13 | ++ `id` (required) - The ID of a project | ||
14 | + | ||
15 | +```json | ||
16 | +[ | ||
17 | + { | ||
18 | + "name": "master", | ||
19 | + "commit": { | ||
20 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
21 | + "parents": [ | ||
22 | + { | ||
23 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
24 | + } | ||
25 | + ], | ||
26 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
27 | + "message": "add projects API", | ||
28 | + "author": { | ||
29 | + "name": "John Smith", | ||
30 | + "email": "john@example.com" | ||
31 | + }, | ||
32 | + "committer": { | ||
33 | + "name": "John Smith", | ||
34 | + "email": "john@example.com" | ||
35 | + }, | ||
36 | + "authored_date": "2012-06-27T05:51:39-07:00", | ||
37 | + "committed_date": "2012-06-28T03:44:20-07:00" | ||
38 | + }, | ||
39 | + "protected": true | ||
40 | + } | ||
41 | +] | ||
42 | +``` | ||
43 | + | ||
44 | + | ||
45 | +## Get single repository branch | ||
46 | + | ||
47 | +Get a single project repository branch. | ||
48 | + | ||
49 | +``` | ||
50 | +GET /projects/:id/repository/branches/:branch | ||
51 | +``` | ||
52 | + | ||
53 | +Parameters: | ||
54 | + | ||
55 | ++ `id` (required) - The ID of a project | ||
56 | ++ `branch` (required) - The name of the branch | ||
57 | + | ||
58 | +```json | ||
59 | +{ | ||
60 | + "name": "master", | ||
61 | + "commit": { | ||
62 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
63 | + "parents": [ | ||
64 | + { | ||
65 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
66 | + } | ||
67 | + ], | ||
68 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
69 | + "message": "add projects API", | ||
70 | + "author": { | ||
71 | + "name": "John Smith", | ||
72 | + "email": "john@example.com" | ||
73 | + }, | ||
74 | + "committer": { | ||
75 | + "name": "John Smith", | ||
76 | + "email": "john@example.com" | ||
77 | + }, | ||
78 | + "authored_date": "2012-06-27T05:51:39-07:00", | ||
79 | + "committed_date": "2012-06-28T03:44:20-07:00" | ||
80 | + }, | ||
81 | + "protected": true | ||
82 | +} | ||
83 | +``` | ||
84 | + | ||
85 | + | ||
86 | +## Protect repository branch | ||
87 | + | ||
88 | +Protects a single project repository branch. This is an idempotent function, protecting an already | ||
89 | +protected repository branch still returns a `200 Ok` status code. | ||
90 | + | ||
91 | +``` | ||
92 | +PUT /projects/:id/repository/branches/:branch/protect | ||
93 | +``` | ||
94 | + | ||
95 | +Parameters: | ||
96 | + | ||
97 | ++ `id` (required) - The ID of a project | ||
98 | ++ `branch` (required) - The name of the branch | ||
99 | + | ||
100 | +```json | ||
101 | +{ | ||
102 | + "name": "master", | ||
103 | + "commit": { | ||
104 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
105 | + "parents": [ | ||
106 | + { | ||
107 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
108 | + } | ||
109 | + ], | ||
110 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
111 | + "message": "add projects API", | ||
112 | + "author": { | ||
113 | + "name": "John Smith", | ||
114 | + "email": "john@example.com" | ||
115 | + }, | ||
116 | + "committer": { | ||
117 | + "name": "John Smith", | ||
118 | + "email": "john@example.com" | ||
119 | + }, | ||
120 | + "authored_date": "2012-06-27T05:51:39-07:00", | ||
121 | + "committed_date": "2012-06-28T03:44:20-07:00" | ||
122 | + }, | ||
123 | + "protected": true | ||
124 | +} | ||
125 | +``` | ||
126 | + | ||
127 | + | ||
128 | +## Unprotect repository branch | ||
129 | + | ||
130 | +Unprotects a single project repository branch. This is an idempotent function, unprotecting an already | ||
131 | +unprotected repository branch still returns a `200 Ok` status code. | ||
132 | + | ||
133 | +``` | ||
134 | +PUT /projects/:id/repository/branches/:branch/unprotect | ||
135 | +``` | ||
136 | + | ||
137 | +Parameters: | ||
138 | + | ||
139 | ++ `id` (required) - The ID of a project | ||
140 | ++ `branch` (required) - The name of the branch | ||
141 | + | ||
142 | +```json | ||
143 | +{ | ||
144 | + "name": "master", | ||
145 | + "commit": { | ||
146 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
147 | + "parents": [ | ||
148 | + { | ||
149 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
150 | + } | ||
151 | + ], | ||
152 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
153 | + "message": "add projects API", | ||
154 | + "author": { | ||
155 | + "name": "John Smith", | ||
156 | + "email": "john@example.com" | ||
157 | + }, | ||
158 | + "committer": { | ||
159 | + "name": "John Smith", | ||
160 | + "email": "john@example.com" | ||
161 | + }, | ||
162 | + "authored_date": "2012-06-27T05:51:39-07:00", | ||
163 | + "committed_date": "2012-06-28T03:44:20-07:00" | ||
164 | + }, | ||
165 | + "protected": false | ||
166 | +} | ||
167 | +``` | ||
168 | + | ||
169 | +## Create repository branch | ||
170 | + | ||
171 | + | ||
172 | +``` | ||
173 | +POST /projects/:id/repository/branches | ||
174 | +``` | ||
175 | + | ||
176 | +Parameters: | ||
177 | + | ||
178 | ++ `id` (required) - The ID of a project | ||
179 | ++ `branch_name` (required) - The name of the branch | ||
180 | ++ `ref` (required) - Create branch from commit sha or existing branch | ||
181 | + | ||
182 | +```json | ||
183 | +{ | ||
184 | + "name": "my-new-branch", | ||
185 | + "commit": { | ||
186 | + "id": "8848c0e90327a0b70f1865b843fb2fbfb9345e57", | ||
187 | + "message": "Merge pull request #54 from brightbox/use_fog_brightbox_module\n\nUpdate to use fog-brightbox module", | ||
188 | + "parent_ids": ["fff449e0bf453576f16c91d6544f00a2664009d8", "f93a93626fec20fd659f4ed3ab2e64019b6169ae"], | ||
189 | + "authored_date": "2014-02-20T19:54:55+02:00", | ||
190 | + "author_name": "john smith", | ||
191 | + "author_email": "john@example.com", | ||
192 | + "committed_date": "2014-02-20T19:54:55+02:00", | ||
193 | + "committer_name": "john smith", | ||
194 | + "committer_email": "john@example.com" | ||
195 | + }, | ||
196 | + "protected": false | ||
197 | +} | ||
198 | +``` |
doc/api/repositories.md
1 | -## List repository branches | ||
2 | - | ||
3 | -Get a list of repository branches from a project, sorted by name alphabetically. | ||
4 | - | ||
5 | -``` | ||
6 | -GET /projects/:id/repository/branches | ||
7 | -``` | ||
8 | - | ||
9 | -Parameters: | ||
10 | - | ||
11 | -+ `id` (required) - The ID of a project | ||
12 | - | ||
13 | -```json | ||
14 | -[ | ||
15 | - { | ||
16 | - "name": "master", | ||
17 | - "commit": { | ||
18 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
19 | - "parents": [ | ||
20 | - { | ||
21 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
22 | - } | ||
23 | - ], | ||
24 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
25 | - "message": "add projects API", | ||
26 | - "author": { | ||
27 | - "name": "John Smith", | ||
28 | - "email": "john@example.com" | ||
29 | - }, | ||
30 | - "committer": { | ||
31 | - "name": "John Smith", | ||
32 | - "email": "john@example.com" | ||
33 | - }, | ||
34 | - "authored_date": "2012-06-27T05:51:39-07:00", | ||
35 | - "committed_date": "2012-06-28T03:44:20-07:00" | ||
36 | - }, | ||
37 | - "protected": true | ||
38 | - } | ||
39 | -] | ||
40 | -``` | ||
41 | - | ||
42 | - | ||
43 | -## Get single repository branch | ||
44 | - | ||
45 | -Get a single project repository branch. | ||
46 | - | ||
47 | -``` | ||
48 | -GET /projects/:id/repository/branches/:branch | ||
49 | -``` | ||
50 | - | ||
51 | -Parameters: | ||
52 | - | ||
53 | -+ `id` (required) - The ID of a project | ||
54 | -+ `branch` (required) - The name of the branch | ||
55 | - | ||
56 | -```json | ||
57 | -{ | ||
58 | - "name": "master", | ||
59 | - "commit": { | ||
60 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
61 | - "parents": [ | ||
62 | - { | ||
63 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
64 | - } | ||
65 | - ], | ||
66 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
67 | - "message": "add projects API", | ||
68 | - "author": { | ||
69 | - "name": "John Smith", | ||
70 | - "email": "john@example.com" | ||
71 | - }, | ||
72 | - "committer": { | ||
73 | - "name": "John Smith", | ||
74 | - "email": "john@example.com" | ||
75 | - }, | ||
76 | - "authored_date": "2012-06-27T05:51:39-07:00", | ||
77 | - "committed_date": "2012-06-28T03:44:20-07:00" | ||
78 | - }, | ||
79 | - "protected": true | ||
80 | -} | ||
81 | -``` | ||
82 | - | ||
83 | - | ||
84 | -## Protect repository branch | ||
85 | - | ||
86 | -Protects a single project repository branch. This is an idempotent function, protecting an already | ||
87 | -protected repository branch still returns a `200 Ok` status code. | ||
88 | - | ||
89 | -``` | ||
90 | -PUT /projects/:id/repository/branches/:branch/protect | ||
91 | -``` | ||
92 | - | ||
93 | -Parameters: | ||
94 | - | ||
95 | -+ `id` (required) - The ID of a project | ||
96 | -+ `branch` (required) - The name of the branch | ||
97 | - | ||
98 | -```json | ||
99 | -{ | ||
100 | - "name": "master", | ||
101 | - "commit": { | ||
102 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
103 | - "parents": [ | ||
104 | - { | ||
105 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
106 | - } | ||
107 | - ], | ||
108 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
109 | - "message": "add projects API", | ||
110 | - "author": { | ||
111 | - "name": "John Smith", | ||
112 | - "email": "john@example.com" | ||
113 | - }, | ||
114 | - "committer": { | ||
115 | - "name": "John Smith", | ||
116 | - "email": "john@example.com" | ||
117 | - }, | ||
118 | - "authored_date": "2012-06-27T05:51:39-07:00", | ||
119 | - "committed_date": "2012-06-28T03:44:20-07:00" | ||
120 | - }, | ||
121 | - "protected": true | ||
122 | -} | ||
123 | -``` | ||
124 | - | ||
125 | - | ||
126 | -## Unprotect repository branch | ||
127 | - | ||
128 | -Unprotects a single project repository branch. This is an idempotent function, unprotecting an already | ||
129 | -unprotected repository branch still returns a `200 Ok` status code. | ||
130 | - | ||
131 | -``` | ||
132 | -PUT /projects/:id/repository/branches/:branch/unprotect | ||
133 | -``` | ||
134 | - | ||
135 | -Parameters: | ||
136 | - | ||
137 | -+ `id` (required) - The ID of a project | ||
138 | -+ `branch` (required) - The name of the branch | ||
139 | - | ||
140 | -```json | ||
141 | -{ | ||
142 | - "name": "master", | ||
143 | - "commit": { | ||
144 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | ||
145 | - "parents": [ | ||
146 | - { | ||
147 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | ||
148 | - } | ||
149 | - ], | ||
150 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | ||
151 | - "message": "add projects API", | ||
152 | - "author": { | ||
153 | - "name": "John Smith", | ||
154 | - "email": "john@example.com" | ||
155 | - }, | ||
156 | - "committer": { | ||
157 | - "name": "John Smith", | ||
158 | - "email": "john@example.com" | ||
159 | - }, | ||
160 | - "authored_date": "2012-06-27T05:51:39-07:00", | ||
161 | - "committed_date": "2012-06-28T03:44:20-07:00" | ||
162 | - }, | ||
163 | - "protected": false | ||
164 | -} | ||
165 | -``` | ||
166 | - | ||
167 | - | ||
168 | ## List project repository tags | 1 | ## List project repository tags |
169 | 2 | ||
170 | Get a list of repository tags from a project, sorted by name in reverse alphabetical order. | 3 | Get a list of repository tags from a project, sorted by name in reverse alphabetical order. |
doc/api/users.md
doc/development/architecture.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 | ||
28 | 28 | ||
29 | ## Components | 29 | ## Components |
30 | 30 | ||
31 | - | 31 | + |
32 | 32 | ||
33 | A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS. | 33 | A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS. |
34 | It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. | 34 | 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 | @@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production | ||
180 | ``` | 180 | ``` |
181 | 181 | ||
182 | Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`. | 182 | Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`. |
183 | -While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL. | 183 | -While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL. |
184 | +While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL. | ||
184 | \ No newline at end of file | 185 | \ No newline at end of file |
doc/install/database_mysql.md
@@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se | @@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se | ||
6 | 6 | ||
7 | # Install the database packages | 7 | # Install the database packages |
8 | sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev | 8 | sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev |
9 | + | ||
10 | + # Ensure you have MySQL version 5.5.14 or later | ||
11 | + mysql --version | ||
9 | 12 | ||
10 | # Pick a database root password (can be anything), type it and press enter | 13 | # Pick a database root password (can be anything), type it and press enter |
11 | # Retype the database root password and press enter | 14 | # 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 | ||
23 | # change $password in the command below to a real password you pick | 26 | # change $password in the command below to a real password you pick |
24 | mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; | 27 | mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; |
25 | 28 | ||
29 | + # Ensure you can use the InnoDB engine which is necessary to support long indexes. | ||
30 | + # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off" | ||
31 | + mysql> SET storage_engine=INNODB; | ||
32 | + | ||
26 | # Create the GitLab production database | 33 | # Create the GitLab production database |
27 | mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; | 34 | mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; |
28 | 35 |
doc/permissions/permissions.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. | ||
38 | |------|-----|--------|---------|------|-----| | 38 | |------|-----|--------|---------|------|-----| |
39 | |Browse group|✓|✓|✓|✓|✓| | 39 | |Browse group|✓|✓|✓|✓|✓| |
40 | |Edit group|||||✓| | 40 | |Edit group|||||✓| |
41 | -|create project in group|||||✓| | 41 | +|Create project in group|||||✓| |
42 | |Manage group members|||||✓| | 42 | |Manage group members|||||✓| |
43 | |Remove group|||||✓| | 43 | |Remove group|||||✓| |
44 | 44 |
doc/ssh/deploy_keys.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 | ||
9 | 9 | ||
10 | You can't add the same deploy key twice with the 'New Deploy Key' option. | 10 | You can't add the same deploy key twice with the 'New Deploy Key' option. |
11 | 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'. | 11 | 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'. |
12 | -You need to be the owner of the deploy key to see it in this list. | 12 | +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. |
doc/update/6.0-to-6.7.md
@@ -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 | ||
140 | cd /home/git/gitlab | 140 | cd /home/git/gitlab |
141 | sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production | 141 | sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production |
142 | ``` | 142 | ``` |
143 | + | ||
144 | +## Login issues after upgrade? | ||
145 | +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) |
doc/update/mysql-to-postgresql.md
@@ -1,9 +0,0 @@ | @@ -1,9 +0,0 @@ | ||
1 | -# Use the shell commands below to convert a MySQL GitLab database to a PostgreSQL one. | ||
2 | - | ||
3 | -``` | ||
4 | -git clone https://github.com/lanyrd/mysql-postgresql-converter.git | ||
5 | -cd mysql-postgresql-converter | ||
6 | -mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production | ||
7 | -python db_converter.py databasename.mysql databasename.psql | ||
8 | -psql -f databasename.psql -d gitlabhq_production | ||
9 | -``` |
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | +# Use the shell commands below to convert a MySQL GitLab database to a PostgreSQL one. | ||
2 | + | ||
3 | +``` | ||
4 | +git clone https://github.com/lanyrd/mysql-postgresql-converter.git | ||
5 | +cd mysql-postgresql-converter | ||
6 | +mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production | ||
7 | +python db_converter.py databasename.mysql databasename.psql | ||
8 | +psql -f databasename.psql -d gitlabhq_production | ||
9 | +``` |
doc/workflow/workflow.md
@@ -3,24 +3,25 @@ | @@ -3,24 +3,25 @@ | ||
3 | ```bash | 3 | ```bash |
4 | git clone git@example.com:project-name.git | 4 | git clone git@example.com:project-name.git |
5 | ``` | 5 | ``` |
6 | + | ||
6 | 2. Create branch with your feature | 7 | 2. Create branch with your feature |
7 | 8 | ||
8 | ```bash | 9 | ```bash |
9 | git checkout -b $feature_name | 10 | git checkout -b $feature_name |
10 | ``` | 11 | ``` |
11 | 12 | ||
12 | -3. Write code. Comit changes | 13 | +3. Write code. Commit changes |
13 | 14 | ||
14 | ```bash | 15 | ```bash |
15 | git commit -am "My feature is ready" | 16 | git commit -am "My feature is ready" |
16 | ``` | 17 | ``` |
17 | 18 | ||
18 | 4. Push your branch to GitLab | 19 | 4. Push your branch to GitLab |
19 | - | 20 | + |
20 | ```bash | 21 | ```bash |
21 | git push origin $feature_name | 22 | git push origin $feature_name |
22 | ``` | 23 | ``` |
23 | 24 | ||
24 | -5. Review your code on Commits page | 25 | +5. Review your code on commits page |
25 | 6. Create a merge request | 26 | 6. Create a merge request |
26 | 7. Your team lead will review the code & merge it to the main branch | 27 | 7. Your team lead will review the code & merge it to the main branch |
lib/api/api.rb
@@ -0,0 +1,85 @@ | @@ -0,0 +1,85 @@ | ||
1 | +require 'mime/types' | ||
2 | + | ||
3 | +module API | ||
4 | + # Projects API | ||
5 | + class Branches < Grape::API | ||
6 | + before { authenticate! } | ||
7 | + before { authorize! :download_code, user_project } | ||
8 | + | ||
9 | + resource :projects do | ||
10 | + # Get a project repository branches | ||
11 | + # | ||
12 | + # Parameters: | ||
13 | + # id (required) - The ID of a project | ||
14 | + # Example Request: | ||
15 | + # GET /projects/:id/repository/branches | ||
16 | + get ":id/repository/branches" do | ||
17 | + present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project | ||
18 | + end | ||
19 | + | ||
20 | + # Get a single branch | ||
21 | + # | ||
22 | + # Parameters: | ||
23 | + # id (required) - The ID of a project | ||
24 | + # branch (required) - The name of the branch | ||
25 | + # Example Request: | ||
26 | + # GET /projects/:id/repository/branches/:branch | ||
27 | + get ":id/repository/branches/:branch" do | ||
28 | + @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } | ||
29 | + not_found!("Branch does not exist") if @branch.nil? | ||
30 | + present @branch, with: Entities::RepoObject, project: user_project | ||
31 | + end | ||
32 | + | ||
33 | + # Protect a single branch | ||
34 | + # | ||
35 | + # Parameters: | ||
36 | + # id (required) - The ID of a project | ||
37 | + # branch (required) - The name of the branch | ||
38 | + # Example Request: | ||
39 | + # PUT /projects/:id/repository/branches/:branch/protect | ||
40 | + put ":id/repository/branches/:branch/protect" do | ||
41 | + authorize_admin_project | ||
42 | + | ||
43 | + @branch = user_project.repository.find_branch(params[:branch]) | ||
44 | + not_found! unless @branch | ||
45 | + protected_branch = user_project.protected_branches.find_by(name: @branch.name) | ||
46 | + user_project.protected_branches.create(name: @branch.name) unless protected_branch | ||
47 | + | ||
48 | + present @branch, with: Entities::RepoObject, project: user_project | ||
49 | + end | ||
50 | + | ||
51 | + # Unprotect a single branch | ||
52 | + # | ||
53 | + # Parameters: | ||
54 | + # id (required) - The ID of a project | ||
55 | + # branch (required) - The name of the branch | ||
56 | + # Example Request: | ||
57 | + # PUT /projects/:id/repository/branches/:branch/unprotect | ||
58 | + put ":id/repository/branches/:branch/unprotect" do | ||
59 | + authorize_admin_project | ||
60 | + | ||
61 | + @branch = user_project.repository.find_branch(params[:branch]) | ||
62 | + not_found! unless @branch | ||
63 | + protected_branch = user_project.protected_branches.find_by(name: @branch.name) | ||
64 | + protected_branch.destroy if protected_branch | ||
65 | + | ||
66 | + present @branch, with: Entities::RepoObject, project: user_project | ||
67 | + end | ||
68 | + | ||
69 | + # Create branch | ||
70 | + # | ||
71 | + # Parameters: | ||
72 | + # id (required) - The ID of a project | ||
73 | + # branch_name (required) - The name of the branch | ||
74 | + # ref (required) - Create branch from commit sha or existing branch | ||
75 | + # Example Request: | ||
76 | + # POST /projects/:id/repository/branches | ||
77 | + post ":id/repository/branches" do | ||
78 | + authorize_push_project | ||
79 | + @branch = CreateBranchService.new.execute(user_project, params[:branch_name], params[:ref], current_user) | ||
80 | + | ||
81 | + present @branch, with: Entities::RepoObject, project: user_project | ||
82 | + end | ||
83 | + end | ||
84 | + end | ||
85 | +end |
lib/api/commits.rb
1 | require 'mime/types' | 1 | require 'mime/types' |
2 | 2 | ||
3 | module API | 3 | module API |
4 | - # Projects API | 4 | + # Projects commits API |
5 | class Commits < Grape::API | 5 | class Commits < Grape::API |
6 | before { authenticate! } | 6 | before { authenticate! } |
7 | before { authorize! :download_code, user_project } | 7 | before { authorize! :download_code, user_project } |
8 | 8 | ||
9 | resource :projects do | 9 | resource :projects do |
10 | - helpers do | ||
11 | - def handle_project_member_errors(errors) | ||
12 | - if errors[:project_access].any? | ||
13 | - error!(errors[:project_access], 422) | ||
14 | - end | ||
15 | - not_found! | ||
16 | - end | ||
17 | - end | ||
18 | - | ||
19 | # Get a project repository commits | 10 | # Get a project repository commits |
20 | # | 11 | # |
21 | # Parameters: | 12 | # Parameters: |
lib/api/helpers.rb
@@ -56,8 +56,12 @@ module API | @@ -56,8 +56,12 @@ module API | ||
56 | end | 56 | end |
57 | end | 57 | end |
58 | 58 | ||
59 | - def paginate(object) | ||
60 | - object.page(params[:page]).per(params[:per_page].to_i) | 59 | + def paginate(relation) |
60 | + per_page = params[:per_page].to_i | ||
61 | + paginated = relation.page(params[:page]).per(per_page) | ||
62 | + add_pagination_headers(paginated, per_page) | ||
63 | + | ||
64 | + paginated | ||
61 | end | 65 | end |
62 | 66 | ||
63 | def authenticate! | 67 | def authenticate! |
@@ -74,6 +78,10 @@ module API | @@ -74,6 +78,10 @@ module API | ||
74 | end | 78 | end |
75 | end | 79 | end |
76 | 80 | ||
81 | + def authorize_push_project | ||
82 | + authorize! :push_code, user_project | ||
83 | + end | ||
84 | + | ||
77 | def authorize_admin_project | 85 | def authorize_admin_project |
78 | authorize! :admin_project, user_project | 86 | authorize! :admin_project, user_project |
79 | end | 87 | end |
@@ -134,6 +142,18 @@ module API | @@ -134,6 +142,18 @@ module API | ||
134 | 142 | ||
135 | private | 143 | private |
136 | 144 | ||
145 | + def add_pagination_headers(paginated, per_page) | ||
146 | + request_url = request.url.split('?').first | ||
147 | + | ||
148 | + links = [] | ||
149 | + links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page? | ||
150 | + links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page? | ||
151 | + links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first") | ||
152 | + links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last") | ||
153 | + | ||
154 | + header 'Link', links.join(', ') | ||
155 | + end | ||
156 | + | ||
137 | def abilities | 157 | def abilities |
138 | @abilities ||= begin | 158 | @abilities ||= begin |
139 | abilities = Six.new | 159 | abilities = Six.new |
lib/api/repositories.rb
@@ -15,66 +15,6 @@ module API | @@ -15,66 +15,6 @@ module API | ||
15 | not_found! | 15 | not_found! |
16 | end | 16 | end |
17 | end | 17 | end |
18 | - | ||
19 | - # Get a project repository branches | ||
20 | - # | ||
21 | - # Parameters: | ||
22 | - # id (required) - The ID of a project | ||
23 | - # Example Request: | ||
24 | - # GET /projects/:id/repository/branches | ||
25 | - get ":id/repository/branches" do | ||
26 | - present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project | ||
27 | - end | ||
28 | - | ||
29 | - # Get a single branch | ||
30 | - # | ||
31 | - # Parameters: | ||
32 | - # id (required) - The ID of a project | ||
33 | - # branch (required) - The name of the branch | ||
34 | - # Example Request: | ||
35 | - # GET /projects/:id/repository/branches/:branch | ||
36 | - get ":id/repository/branches/:branch" do | ||
37 | - @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } | ||
38 | - not_found!("Branch does not exist") if @branch.nil? | ||
39 | - present @branch, with: Entities::RepoObject, project: user_project | ||
40 | - end | ||
41 | - | ||
42 | - # Protect a single branch | ||
43 | - # | ||
44 | - # Parameters: | ||
45 | - # id (required) - The ID of a project | ||
46 | - # branch (required) - The name of the branch | ||
47 | - # Example Request: | ||
48 | - # PUT /projects/:id/repository/branches/:branch/protect | ||
49 | - put ":id/repository/branches/:branch/protect" do | ||
50 | - authorize_admin_project | ||
51 | - | ||
52 | - @branch = user_project.repository.find_branch(params[:branch]) | ||
53 | - not_found! unless @branch | ||
54 | - protected_branch = user_project.protected_branches.find_by(name: @branch.name) | ||
55 | - user_project.protected_branches.create(name: @branch.name) unless protected_branch | ||
56 | - | ||
57 | - present @branch, with: Entities::RepoObject, project: user_project | ||
58 | - end | ||
59 | - | ||
60 | - # Unprotect a single branch | ||
61 | - # | ||
62 | - # Parameters: | ||
63 | - # id (required) - The ID of a project | ||
64 | - # branch (required) - The name of the branch | ||
65 | - # Example Request: | ||
66 | - # PUT /projects/:id/repository/branches/:branch/unprotect | ||
67 | - put ":id/repository/branches/:branch/unprotect" do | ||
68 | - authorize_admin_project | ||
69 | - | ||
70 | - @branch = user_project.repository.find_branch(params[:branch]) | ||
71 | - not_found! unless @branch | ||
72 | - protected_branch = user_project.protected_branches.find_by(name: @branch.name) | ||
73 | - protected_branch.destroy if protected_branch | ||
74 | - | ||
75 | - present @branch, with: Entities::RepoObject, project: user_project | ||
76 | - end | ||
77 | - | ||
78 | # Get a project repository tags | 18 | # Get a project repository tags |
79 | # | 19 | # |
80 | # Parameters: | 20 | # Parameters: |
@@ -161,7 +101,7 @@ module API | @@ -161,7 +101,7 @@ module API | ||
161 | repo = user_project.repository | 101 | repo = user_project.repository |
162 | ref = params[:sha] | 102 | ref = params[:sha] |
163 | format = params[:format] | 103 | format = params[:format] |
164 | - storage_path = Rails.root.join("tmp", "repositories") | 104 | + storage_path = Gitlab.config.gitlab.repository_downloads_path |
165 | 105 | ||
166 | file_path = repo.archive_repo(ref, storage_path, format) | 106 | file_path = repo.archive_repo(ref, storage_path, format) |
167 | if file_path && File.exists?(file_path) | 107 | if file_path && File.exists?(file_path) |
lib/backup/database.rb
@@ -29,9 +29,10 @@ module Backup | @@ -29,9 +29,10 @@ module Backup | ||
29 | print "Restoring MySQL database #{config['database']} ... " | 29 | print "Restoring MySQL database #{config['database']} ... " |
30 | system('mysql', *mysql_args, config['database'], in: db_file_name) | 30 | system('mysql', *mysql_args, config['database'], in: db_file_name) |
31 | when "postgresql" then | 31 | when "postgresql" then |
32 | - puts "Destructively rebuilding database schema for RAILS_ENV #{Rails.env}" | ||
33 | - Rake::Task["db:schema:load"].invoke | ||
34 | print "Restoring PostgreSQL database #{config['database']} ... " | 32 | print "Restoring PostgreSQL database #{config['database']} ... " |
33 | + # Drop all tables because PostgreSQL DB dumps do not contain DROP TABLE | ||
34 | + # statements like MySQL. | ||
35 | + Rake::Task["gitlab:db:drop_all_tables"].invoke | ||
35 | pg_env | 36 | pg_env |
36 | system('psql', config['database'], '-f', db_file_name) | 37 | system('psql', config['database'], '-f', db_file_name) |
37 | end | 38 | end |
lib/gitlab/ldap/user.rb
@@ -81,16 +81,17 @@ module Gitlab | @@ -81,16 +81,17 @@ module Gitlab | ||
81 | 81 | ||
82 | private | 82 | private |
83 | 83 | ||
84 | - def find_by_uid(uid) | ||
85 | - model.where(provider: provider, extern_uid: uid).last | 84 | + def find_by_uid_and_provider |
85 | + find_by_uid(uid) | ||
86 | end | 86 | end |
87 | 87 | ||
88 | - def username | ||
89 | - (auth.info.nickname || samaccountname).to_s.force_encoding("utf-8") | 88 | + def find_by_uid(uid) |
89 | + # LDAP distinguished name is case-insensitive | ||
90 | + model.where("provider = ? and lower(extern_uid) = ?", provider, uid.downcase).last | ||
90 | end | 91 | end |
91 | 92 | ||
92 | - def samaccountname | ||
93 | - (auth.extra[:raw_info][:samaccountname] || []).first | 93 | + def username |
94 | + auth.info.nickname.to_s.force_encoding("utf-8") | ||
94 | end | 95 | end |
95 | 96 | ||
96 | def provider | 97 | def provider |
lib/support/init.d/gitlab
@@ -149,7 +149,7 @@ exit_if_not_running(){ | @@ -149,7 +149,7 @@ exit_if_not_running(){ | ||
149 | } | 149 | } |
150 | 150 | ||
151 | ## Starts Unicorn and Sidekiq if they're not running. | 151 | ## Starts Unicorn and Sidekiq if they're not running. |
152 | -start() { | 152 | +start_gitlab() { |
153 | check_stale_pids | 153 | check_stale_pids |
154 | 154 | ||
155 | if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then | 155 | if [ "$web_status" != "0" -a "$sidekiq_status" != "0" ]; then |
@@ -167,7 +167,7 @@ start() { | @@ -167,7 +167,7 @@ start() { | ||
167 | # Remove old socket if it exists | 167 | # Remove old socket if it exists |
168 | rm -f "$socket_path"/gitlab.socket 2>/dev/null | 168 | rm -f "$socket_path"/gitlab.socket 2>/dev/null |
169 | # Start the web server | 169 | # Start the web server |
170 | - RAILS_ENV=$RAILS_ENV script/web start & | 170 | + RAILS_ENV=$RAILS_ENV script/web start |
171 | fi | 171 | fi |
172 | 172 | ||
173 | # If sidekiq is already running, don't start it again. | 173 | # If sidekiq is already running, don't start it again. |
@@ -184,7 +184,7 @@ start() { | @@ -184,7 +184,7 @@ start() { | ||
184 | } | 184 | } |
185 | 185 | ||
186 | ## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them. | 186 | ## Asks the Unicorn and the Sidekiq if they would be so kind as to stop, if not kills them. |
187 | -stop() { | 187 | +stop_gitlab() { |
188 | exit_if_not_running | 188 | exit_if_not_running |
189 | 189 | ||
190 | if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then | 190 | if [ "$web_status" = "0" -a "$sidekiq_status" = "0" ]; then |
@@ -246,7 +246,7 @@ print_status() { | @@ -246,7 +246,7 @@ print_status() { | ||
246 | } | 246 | } |
247 | 247 | ||
248 | ## Tells unicorn to reload it's config and Sidekiq to restart | 248 | ## Tells unicorn to reload it's config and Sidekiq to restart |
249 | -reload(){ | 249 | +reload_gitlab(){ |
250 | exit_if_not_running | 250 | exit_if_not_running |
251 | if [ "$wpid" = "0" ];then | 251 | if [ "$wpid" = "0" ];then |
252 | echo "The GitLab Unicorn Web server is not running thus its configuration can't be reloaded." | 252 | 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(){ | ||
263 | } | 263 | } |
264 | 264 | ||
265 | ## Restarts Sidekiq and Unicorn. | 265 | ## Restarts Sidekiq and Unicorn. |
266 | -restart(){ | 266 | +restart_gitlab(){ |
267 | check_status | 267 | check_status |
268 | if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then | 268 | if [ "$web_status" = "0" -o "$sidekiq_status" = "0" ]; then |
269 | - stop | 269 | + stop_gitlab |
270 | fi | 270 | fi |
271 | - start | 271 | + start_gitlab |
272 | } | 272 | } |
273 | 273 | ||
274 | 274 | ||
@@ -276,16 +276,16 @@ restart(){ | @@ -276,16 +276,16 @@ restart(){ | ||
276 | 276 | ||
277 | case "$1" in | 277 | case "$1" in |
278 | start) | 278 | start) |
279 | - start | 279 | + start_gitlab |
280 | ;; | 280 | ;; |
281 | stop) | 281 | stop) |
282 | - stop | 282 | + stop_gitlab |
283 | ;; | 283 | ;; |
284 | restart) | 284 | restart) |
285 | - restart | 285 | + restart_gitlab |
286 | ;; | 286 | ;; |
287 | reload|force-reload) | 287 | reload|force-reload) |
288 | - reload | 288 | + reload_gitlab |
289 | ;; | 289 | ;; |
290 | status) | 290 | status) |
291 | print_status | 291 | print_status |
lib/tasks/gitlab/check.rake
@@ -677,7 +677,20 @@ namespace :gitlab do | @@ -677,7 +677,20 @@ namespace :gitlab do | ||
677 | end | 677 | end |
678 | 678 | ||
679 | def filter | 679 | def filter |
680 | - Net::LDAP::Filter.present?(ldap_config.uid) | 680 | + uid_filter = Net::LDAP::Filter.present?(ldap_config.uid) |
681 | + if user_filter | ||
682 | + Net::LDAP::Filter.join(uid_filter, user_filter) | ||
683 | + else | ||
684 | + uid_filter | ||
685 | + end | ||
686 | + end | ||
687 | + | ||
688 | + def user_filter | ||
689 | + if ldap_config['user_filter'] && ldap_config.user_filter.present? | ||
690 | + Net::LDAP::Filter.construct(ldap_config.user_filter) | ||
691 | + else | ||
692 | + nil | ||
693 | + end | ||
681 | end | 694 | end |
682 | 695 | ||
683 | def ldap | 696 | def ldap |
spec/models/user_spec.rb
@@ -292,6 +292,20 @@ describe User do | @@ -292,6 +292,20 @@ describe User do | ||
292 | end | 292 | end |
293 | end | 293 | end |
294 | 294 | ||
295 | + describe 'search' do | ||
296 | + let(:user1) { create(:user, username: 'James', email: 'james@testing.com') } | ||
297 | + let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') } | ||
298 | + | ||
299 | + it "should be case insensitive" do | ||
300 | + User.search(user1.username.upcase).to_a.should == [user1] | ||
301 | + User.search(user1.username.downcase).to_a.should == [user1] | ||
302 | + User.search(user2.username.upcase).to_a.should == [user2] | ||
303 | + User.search(user2.username.downcase).to_a.should == [user2] | ||
304 | + User.search(user1.username.downcase).to_a.count.should == 2 | ||
305 | + User.search(user2.username.downcase).to_a.count.should == 1 | ||
306 | + end | ||
307 | + end | ||
308 | + | ||
295 | describe 'by_username_or_id' do | 309 | describe 'by_username_or_id' do |
296 | let(:user1) { create(:user, username: 'foo') } | 310 | let(:user1) { create(:user, username: 'foo') } |
297 | 311 |
@@ -0,0 +1,115 @@ | @@ -0,0 +1,115 @@ | ||
1 | +require 'spec_helper' | ||
2 | +require 'mime/types' | ||
3 | + | ||
4 | +describe API::API do | ||
5 | + include ApiHelpers | ||
6 | + before(:each) { enable_observers } | ||
7 | + after(:each) {disable_observers} | ||
8 | + | ||
9 | + let(:user) { create(:user) } | ||
10 | + let(:user2) { create(:user) } | ||
11 | + let!(:project) { create(:project, creator_id: user.id) } | ||
12 | + let!(:master) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } | ||
13 | + let!(:guest) { create(:users_project, user: user2, project: project, project_access: UsersProject::GUEST) } | ||
14 | + | ||
15 | + describe "GET /projects/:id/repository/branches" do | ||
16 | + it "should return an array of project branches" do | ||
17 | + get api("/projects/#{project.id}/repository/branches", user) | ||
18 | + response.status.should == 200 | ||
19 | + json_response.should be_an Array | ||
20 | + json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name | ||
21 | + end | ||
22 | + end | ||
23 | + | ||
24 | + describe "GET /projects/:id/repository/branches/:branch" do | ||
25 | + it "should return the branch information for a single branch" do | ||
26 | + get api("/projects/#{project.id}/repository/branches/new_design", user) | ||
27 | + response.status.should == 200 | ||
28 | + | ||
29 | + json_response['name'].should == 'new_design' | ||
30 | + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
31 | + json_response['protected'].should == false | ||
32 | + end | ||
33 | + | ||
34 | + it "should return a 403 error if guest" do | ||
35 | + get api("/projects/#{project.id}/repository/branches", user2) | ||
36 | + response.status.should == 403 | ||
37 | + end | ||
38 | + | ||
39 | + it "should return a 404 error if branch is not available" do | ||
40 | + get api("/projects/#{project.id}/repository/branches/unknown", user) | ||
41 | + response.status.should == 404 | ||
42 | + end | ||
43 | + end | ||
44 | + | ||
45 | + describe "PUT /projects/:id/repository/branches/:branch/protect" do | ||
46 | + it "should protect a single branch" do | ||
47 | + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | ||
48 | + response.status.should == 200 | ||
49 | + | ||
50 | + json_response['name'].should == 'new_design' | ||
51 | + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
52 | + json_response['protected'].should == true | ||
53 | + end | ||
54 | + | ||
55 | + it "should return a 404 error if branch not found" do | ||
56 | + put api("/projects/#{project.id}/repository/branches/unknown/protect", user) | ||
57 | + response.status.should == 404 | ||
58 | + end | ||
59 | + | ||
60 | + it "should return a 403 error if guest" do | ||
61 | + put api("/projects/#{project.id}/repository/branches/new_design/protect", user2) | ||
62 | + response.status.should == 403 | ||
63 | + end | ||
64 | + | ||
65 | + it "should return success when protect branch again" do | ||
66 | + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | ||
67 | + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | ||
68 | + response.status.should == 200 | ||
69 | + end | ||
70 | + end | ||
71 | + | ||
72 | + describe "PUT /projects/:id/repository/branches/:branch/unprotect" do | ||
73 | + it "should unprotect a single branch" do | ||
74 | + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | ||
75 | + response.status.should == 200 | ||
76 | + | ||
77 | + json_response['name'].should == 'new_design' | ||
78 | + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
79 | + json_response['protected'].should == false | ||
80 | + end | ||
81 | + | ||
82 | + it "should return success when unprotect branch" do | ||
83 | + put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) | ||
84 | + response.status.should == 404 | ||
85 | + end | ||
86 | + | ||
87 | + it "should return success when unprotect branch again" do | ||
88 | + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | ||
89 | + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | ||
90 | + response.status.should == 200 | ||
91 | + end | ||
92 | + end | ||
93 | + | ||
94 | + | ||
95 | + describe "POST /projects/:id/repository/branches" do | ||
96 | + it "should create a new branch" do | ||
97 | + post api("/projects/#{project.id}/repository/branches", user), | ||
98 | + branch_name: 'new_design', | ||
99 | + ref: '621491c677087aa243f165eab467bfdfbee00be1' | ||
100 | + | ||
101 | + response.status.should == 201 | ||
102 | + | ||
103 | + json_response['name'].should == 'new_design' | ||
104 | + json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
105 | + end | ||
106 | + | ||
107 | + it "should deny for user without push access" do | ||
108 | + post api("/projects/#{project.id}/repository/branches", user2), | ||
109 | + branch_name: 'new_design', | ||
110 | + ref: '621491c677087aa243f165eab467bfdfbee00be1' | ||
111 | + | ||
112 | + response.status.should == 403 | ||
113 | + end | ||
114 | + end | ||
115 | +end |
spec/requests/api/issues_spec.rb
@@ -25,6 +25,12 @@ describe API::API do | @@ -25,6 +25,12 @@ describe API::API do | ||
25 | json_response.should be_an Array | 25 | json_response.should be_an Array |
26 | json_response.first['title'].should == issue.title | 26 | json_response.first['title'].should == issue.title |
27 | end | 27 | end |
28 | + | ||
29 | + it "should add pagination headers" do | ||
30 | + get api("/issues?per_page=3", user) | ||
31 | + response.headers['Link'].should == | ||
32 | + '<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"' | ||
33 | + end | ||
28 | end | 34 | end |
29 | end | 35 | end |
30 | 36 |
spec/requests/api/repositories_spec.rb
@@ -14,86 +14,6 @@ describe API::API do | @@ -14,86 +14,6 @@ describe API::API do | ||
14 | 14 | ||
15 | before { project.team << [user, :reporter] } | 15 | before { project.team << [user, :reporter] } |
16 | 16 | ||
17 | - | ||
18 | - describe "GET /projects/:id/repository/branches" do | ||
19 | - it "should return an array of project branches" do | ||
20 | - get api("/projects/#{project.id}/repository/branches", user) | ||
21 | - response.status.should == 200 | ||
22 | - json_response.should be_an Array | ||
23 | - json_response.first['name'].should == project.repo.heads.sort_by(&:name).first.name | ||
24 | - end | ||
25 | - end | ||
26 | - | ||
27 | - describe "GET /projects/:id/repository/branches/:branch" do | ||
28 | - it "should return the branch information for a single branch" do | ||
29 | - get api("/projects/#{project.id}/repository/branches/new_design", user) | ||
30 | - response.status.should == 200 | ||
31 | - | ||
32 | - json_response['name'].should == 'new_design' | ||
33 | - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
34 | - json_response['protected'].should == false | ||
35 | - end | ||
36 | - | ||
37 | - it "should return a 403 error if guest" do | ||
38 | - get api("/projects/#{project.id}/repository/branches", user2) | ||
39 | - response.status.should == 403 | ||
40 | - end | ||
41 | - | ||
42 | - it "should return a 404 error if branch is not available" do | ||
43 | - get api("/projects/#{project.id}/repository/branches/unknown", user) | ||
44 | - response.status.should == 404 | ||
45 | - end | ||
46 | - end | ||
47 | - | ||
48 | - describe "PUT /projects/:id/repository/branches/:branch/protect" do | ||
49 | - it "should protect a single branch" do | ||
50 | - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | ||
51 | - response.status.should == 200 | ||
52 | - | ||
53 | - json_response['name'].should == 'new_design' | ||
54 | - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
55 | - json_response['protected'].should == true | ||
56 | - end | ||
57 | - | ||
58 | - it "should return a 404 error if branch not found" do | ||
59 | - put api("/projects/#{project.id}/repository/branches/unknown/protect", user) | ||
60 | - response.status.should == 404 | ||
61 | - end | ||
62 | - | ||
63 | - it "should return a 403 error if guest" do | ||
64 | - put api("/projects/#{project.id}/repository/branches/new_design/protect", user2) | ||
65 | - response.status.should == 403 | ||
66 | - end | ||
67 | - | ||
68 | - it "should return success when protect branch again" do | ||
69 | - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | ||
70 | - put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | ||
71 | - response.status.should == 200 | ||
72 | - end | ||
73 | - end | ||
74 | - | ||
75 | - describe "PUT /projects/:id/repository/branches/:branch/unprotect" do | ||
76 | - it "should unprotect a single branch" do | ||
77 | - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | ||
78 | - response.status.should == 200 | ||
79 | - | ||
80 | - json_response['name'].should == 'new_design' | ||
81 | - json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | ||
82 | - json_response['protected'].should == false | ||
83 | - end | ||
84 | - | ||
85 | - it "should return success when unprotect branch" do | ||
86 | - put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) | ||
87 | - response.status.should == 404 | ||
88 | - end | ||
89 | - | ||
90 | - it "should return success when unprotect branch again" do | ||
91 | - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | ||
92 | - put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | ||
93 | - response.status.should == 200 | ||
94 | - end | ||
95 | - end | ||
96 | - | ||
97 | describe "GET /projects/:id/repository/tags" do | 17 | describe "GET /projects/:id/repository/tags" do |
98 | it "should return an array of project tags" do | 18 | it "should return an array of project tags" do |
99 | get api("/projects/#{project.id}/repository/tags", user) | 19 | get api("/projects/#{project.id}/repository/tags", user) |