Commit f5147780 authored by Marc Radulescu's avatar Marc Radulescu

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq into grammar_fixes

parents d1980adf 9f80ab8e
user: git
group: git
before_precompile: ./bin/pkgr_before_precompile.sh
targets:
debian-7: &wheezy
build_dependencies:
- libicu-dev
dependencies:
- libicu48
- libpcre3
- git
ubuntu-12.04: *wheezy
ubuntu-14.04:
build_dependencies:
- libicu-dev
dependencies:
- libicu52
- libpcre3
- git
...@@ -8,6 +8,16 @@ v 6.9.0 ...@@ -8,6 +8,16 @@ v 6.9.0
- Fix syntax highlighting for code comments blocks - Fix syntax highlighting for code comments blocks
- Improve comments loading logic - Improve comments loading logic
- Stop refreshing comments when the tab is hidden - Stop refreshing comments when the tab is hidden
- Improve issue and merge request mobile UI (Drew Blessing)
- Document how to convert a backup to PostgreSQL
- Fix locale bug in backup manager
- Fix can not automerge when MR description is too long
- Fix wiki backup skip bug
- Two Step MR creation process
- Remove unwanted files from satellite working directory with git clean -fdx
- Accept merge request via API (sponsored by O'Reilly Media)
- Add more access checks during API calls
- Block SSH access for 'disabled' Active Directory users
v 6.8.0 v 6.8.0
- Ability to at mention users that are participating in issue and merge req. discussion - Ability to at mention users that are participating in issue and merge req. discussion
......
...@@ -152,7 +152,7 @@ gem "rack-attack" ...@@ -152,7 +152,7 @@ gem "rack-attack"
# Ace editor # Ace editor
gem 'ace-rails-ap' gem 'ace-rails-ap'
gem "sass-rails" gem "sass-rails", '~> 4.0.2'
gem "coffee-rails" gem "coffee-rails"
gem "uglifier" gem "uglifier"
gem "therubyracer" gem "therubyracer"
......
...@@ -2,26 +2,26 @@ GEM ...@@ -2,26 +2,26 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
ace-rails-ap (2.0.1) ace-rails-ap (2.0.1)
actionmailer (4.0.3) actionmailer (4.0.5)
actionpack (= 4.0.3) actionpack (= 4.0.5)
mail (~> 2.5.4) mail (~> 2.5.4)
actionpack (4.0.3) actionpack (4.0.5)
activesupport (= 4.0.3) activesupport (= 4.0.5)
builder (~> 3.1.0) builder (~> 3.1.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rack (~> 1.5.2) rack (~> 1.5.2)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
activemodel (4.0.3) activemodel (4.0.5)
activesupport (= 4.0.3) activesupport (= 4.0.5)
builder (~> 3.1.0) builder (~> 3.1.0)
activerecord (4.0.3) activerecord (4.0.5)
activemodel (= 4.0.3) activemodel (= 4.0.5)
activerecord-deprecated_finders (~> 1.0.2) activerecord-deprecated_finders (~> 1.0.2)
activesupport (= 4.0.3) activesupport (= 4.0.5)
arel (~> 4.0.0) arel (~> 4.0.0)
activerecord-deprecated_finders (1.0.3) activerecord-deprecated_finders (1.0.3)
activesupport (4.0.3) activesupport (4.0.5)
i18n (~> 0.6, >= 0.6.4) i18n (~> 0.6, >= 0.6.9)
minitest (~> 4.2) minitest (~> 4.2)
multi_json (~> 1.3) multi_json (~> 1.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
...@@ -162,7 +162,7 @@ GEM ...@@ -162,7 +162,7 @@ GEM
multi_json multi_json
gitlab-grack (2.0.0.pre) gitlab-grack (2.0.0.pre)
rack (~> 1.5.1) rack (~> 1.5.1)
gitlab-grit (2.6.5) gitlab-grit (2.6.7)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
...@@ -279,7 +279,7 @@ GEM ...@@ -279,7 +279,7 @@ GEM
mime-types (1.25.1) mime-types (1.25.1)
mini_portile (0.5.3) mini_portile (0.5.3)
minitest (4.7.5) minitest (4.7.5)
multi_json (1.9.3) multi_json (1.10.0)
multi_xml (0.5.5) multi_xml (0.5.5)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.11) mysql2 (0.3.11)
...@@ -349,13 +349,13 @@ GEM ...@@ -349,13 +349,13 @@ GEM
rack rack
rack-test (0.6.2) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
rails (4.0.3) rails (4.0.5)
actionmailer (= 4.0.3) actionmailer (= 4.0.5)
actionpack (= 4.0.3) actionpack (= 4.0.5)
activerecord (= 4.0.3) activerecord (= 4.0.5)
activesupport (= 4.0.3) activesupport (= 4.0.5)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.0.3) railties (= 4.0.5)
sprockets-rails (~> 2.0.0) sprockets-rails (~> 2.0.0)
rails-observers (0.1.2) rails-observers (0.1.2)
activemodel (~> 4.0) activemodel (~> 4.0)
...@@ -368,9 +368,9 @@ GEM ...@@ -368,9 +368,9 @@ GEM
i18n i18n
require_all require_all
ruby-progressbar ruby-progressbar
railties (4.0.3) railties (4.0.5)
actionpack (= 4.0.3) actionpack (= 4.0.5)
activesupport (= 4.0.3) activesupport (= 4.0.5)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
raindrops (0.12.0) raindrops (0.12.0)
...@@ -427,11 +427,12 @@ GEM ...@@ -427,11 +427,12 @@ GEM
safe_yaml (0.9.7) safe_yaml (0.9.7)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
sass (3.2.12) sass (3.2.19)
sass-rails (4.0.1) sass-rails (4.0.3)
railties (>= 4.0.0, < 5.0) railties (>= 4.0.0, < 5.0)
sass (>= 3.1.10) sass (~> 3.2.0)
sprockets-rails (~> 2.0.0) sprockets (~> 2.8, <= 2.11.0)
sprockets-rails (~> 2.0)
sdoc (0.3.20) sdoc (0.3.20)
json (>= 1.1.3) json (>= 1.1.3)
rdoc (~> 3.10) rdoc (~> 3.10)
...@@ -478,7 +479,7 @@ GEM ...@@ -478,7 +479,7 @@ GEM
spring (>= 0.9.1) spring (>= 0.9.1)
spring-commands-spinach (1.0.0) spring-commands-spinach (1.0.0)
spring (>= 0.9.1) spring (>= 0.9.1)
sprockets (2.10.1) sprockets (2.11.0)
hike (~> 1.2) hike (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
...@@ -636,7 +637,7 @@ DEPENDENCIES ...@@ -636,7 +637,7 @@ DEPENDENCIES
redis-rails redis-rails
rspec-rails rspec-rails
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails sass-rails (~> 4.0.2)
sdoc sdoc
seed-fu seed-fu
select2-rails select2-rails
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
### Canonical source ### Canonical source
* The source of GitLab Communinity Edition is [hosted on GitLab Cloud](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. * The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
### Code status ### Code status
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq) * [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
* [![PullReview stats](https://www.pullreview.com/gitlab/gitlab-org/gitlab-ce/badges/master.svg?)](https://www.pullreview.com/gitlab.gitlab.com/gitlab-org/gitlab-ce/reviews/master)
### Resources ### Resources
* [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/). * [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/).
...@@ -33,7 +35,7 @@ ...@@ -33,7 +35,7 @@
* [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab. * [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab.
* Unofficial third-party [iPhone app](http://gitlabcontrol.com/)m [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) and [command line client](https://github.com/drewblessing/gitlab-cli) for GitLab. * Unofficial third-party [iPhone app](http://gitlabcontrol.com/), [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) and [command line client](https://github.com/drewblessing/gitlab-cli) and [Ruby API wrapper](https://github.com/NARKOZ/gitlab) for GitLab.
### Requirements ### Requirements
...@@ -51,7 +53,7 @@ ...@@ -51,7 +53,7 @@
* [GitLab packages](https://www.gitlab.com/downloads/) **recommended** These packages contain GitLab and all its depencies (Ruby, PostgreSQL, Redis, Nginx, Unicorn, etc.). They are made with [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md) that also contains the installation instructions. * [GitLab packages](https://www.gitlab.com/downloads/) **recommended** These packages contain GitLab and all its depencies (Ruby, PostgreSQL, Redis, Nginx, Unicorn, etc.). They are made with [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md) that also contains the installation instructions.
* [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation on a virtual machine with Vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies. * [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies.
* [Manual installation guide](doc/install/installation.md) This guide to set up a production server on Ubuntu offers detailed and complete step-by-step instructions. * [Manual installation guide](doc/install/installation.md) This guide to set up a production server on Ubuntu offers detailed and complete step-by-step instructions.
...@@ -61,6 +63,8 @@ ...@@ -61,6 +63,8 @@
* [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.). * [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.).
* [Cloud 66 deployment and management](http://blog.cloud66.com/installing-gitlab-ubuntu/) Use Cloud 66 to deploy GitLab to your own server or any cloud (eg. DigitalOcean, AWS, Rackspace, GCE) and then manage it with database backups, scaling and more.
#### Unofficial installation methods #### Unofficial installation methods
* [GitLab recipes](https://gitlab.com/gitlab-org/gitlab-recipes/) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version. * [GitLab recipes](https://gitlab.com/gitlab-org/gitlab-recipes/) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version.
......
6.9.0.pre 6.9.0.rc1
...@@ -53,6 +53,12 @@ class Notes ...@@ -53,6 +53,12 @@ class Notes
# fetch notes when tab becomes visible # fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange $(document).on "visibilitychange", @visibilityChange
@notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
$(document).on('keypress', @notes_forms, (e)->
if e.keyCode == 10 || (e.ctrlKey && e.keyCode == 13)
$(@).parents('form').submit()
)
cleanBinding: -> cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form" $(document).off "ajax:success", ".js-discussion-note-form"
...@@ -67,6 +73,7 @@ class Notes ...@@ -67,6 +73,7 @@ class Notes
$(document).off "click", ".js-discussion-reply-button" $(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button" $(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange" $(document).off "visibilitychange"
$(document).off "keypress", @notes_forms
initRefresh: -> initRefresh: ->
......
@projectUsersSelect = @projectUsersSelect =
init: -> init: ->
$('.ajax-project-users-select').each (i, select) -> $('.ajax-project-users-select').each (i, select) ->
project_id = $('body').data('project-id') project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2 $(select).select2
placeholder: $(select).data('placeholder') || "Search for a user" placeholder: $(select).data('placeholder') || "Search for a user"
......
...@@ -11,14 +11,11 @@ ...@@ -11,14 +11,11 @@
} }
.file-title { .file-title {
background: #DDD; background: #EEE;
border-bottom: 1px solid #CCC; border-bottom: 1px solid #CCC;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
margin: 0; margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left; text-align: left;
color: $style_color;
padding: 9px 10px; padding: 9px 10px;
.options { .options {
...@@ -31,12 +28,15 @@ ...@@ -31,12 +28,15 @@
} }
.file_name { .file_name {
color: $style_color; font-weight: bold;
padding-left: 3px;
font-size: 14px; font-size: 14px;
text-shadow: 0 1px 1px #fff;
small { small {
color: #999; color: #888;
font-size: 13px; font-size: 13px;
font-weight: normal;
padding-left: 10px;
} }
} }
} }
......
...@@ -75,3 +75,26 @@ label { ...@@ -75,3 +75,26 @@ label {
width: 200px; width: 200px;
} }
} }
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
.max-width-marker {
color: rgba(0, 0, 0, 0.0);
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, white);
position: absolute;
z-index: 1;
}
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
...@@ -12,41 +12,42 @@ ...@@ -12,41 +12,42 @@
margin:20px 0; margin:20px 0;
background: #FFF; background: #FFF;
border: 1px solid #EEE; border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
&.issue-box-closed { &.issue-box-closed {
border-color: #DA4E49; border-color: $border_danger;
.state { .state {
background-color: #f2dede; background-color: $bg_light_danger;
border-color: #ebccd1; border-color: $border_danger;
color: #a94442; color: $color_danger;
.state-label { .state-label {
background: #DA4E49; background-color: $bg_danger;
color: #FFF; color: #FFF;
} }
} }
} }
&.issue-box-merged { &.issue-box-merged {
border-color: #31708f; border-color: $border_primary;
.state { .state {
background-color: #d9edf7; background-color: $bg_light_primary;
border-color: #bce8f1; border-color: $border_primary;
color: #31708f; color: $color_primary;
.state-label { .state-label {
background: #31708f; background-color: $bg_primary;
color: #FFF; color: #FFF;
} }
} }
} }
&.issue-box-open { &.issue-box-open {
border-color: #4A4; border-color: $border_success;
.state { .state {
background-color: #dff0d8; background-color: $bg_light_success;
border-color: #d6e9c6; border-color: $border_success;
color: #3c763d; color: $color_success;
.state-label { .state-label {
background: #4A4; background-color: $bg_success;
color: #FFF; color: #FFF;
} }
} }
...@@ -70,7 +71,6 @@ ...@@ -70,7 +71,6 @@
} }
.state { .state {
height: 34px;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #DDD;
line-height: 32px; line-height: 32px;
} }
...@@ -89,6 +89,18 @@ ...@@ -89,6 +89,18 @@
border: none; border: none;
border-top: 1px solid #eee; border-top: 1px solid #eee;
padding: 15px 25px; padding: 15px 25px;
// Reset text align for children
.text-right > * { text-align: left; }
@media (max-width: $screen-xs-max) {
// Don't right align on mobile
.text-right { text-align: left; }
.row .col-md-6 {
padding-top: 5px;
}
}
} }
.description { .description {
...@@ -106,7 +118,11 @@ ...@@ -106,7 +118,11 @@
padding: 1px 25px; padding: 1px 25px;
text-align: center; text-align: center;
text-shadow: none; text-shadow: none;
margin-right: 20px;
display: inline-block; display: inline-block;
line-height: 34px;
}
.creator {
padding: 2px 15px;
} }
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
width: 270px; width: 270px;
.ui-datepicker-header { .ui-datepicker-header {
background: #EEE; background: #FFF;
border-color: #DDD; border-color: #DDD;
} }
...@@ -19,20 +19,37 @@ ...@@ -19,20 +19,37 @@
} }
&.ui-autocomplete { &.ui-autocomplete {
@include border-radius(0px);
border-color: #DDD; border-color: #DDD;
padding: 0; padding: 0;
margin-top: 2px;
z-index: 1001;
.ui-menu-item a { .ui-menu-item a {
color: #777; padding: 4px 10px;
&:hover {
background: $hover;
border-color: $primary_color;
@include border-radius(0px);
color: #333;
}
} }
} }
}
.ui-state-default {
border: 1px solid #FFF;
background: #FFF;
color: #777;
}
.ui-state-highlight {
border: 1px solid #EEE;
background: #EEE;
}
.ui-state-active {
border: 1px solid $bg_style_color;
background: $bg_style_color;
color: #FFF;
}
.ui-state-hover,
.ui-state-focus {
border: 1px solid $hover;
background: $hover;
color: #333;
}
}
...@@ -47,7 +47,7 @@ a { ...@@ -47,7 +47,7 @@ a {
text-decoration: underline; text-decoration: underline;
} }
&.dark { &.darken {
color: $style_color; color: $style_color;
} }
......
...@@ -41,31 +41,6 @@ ...@@ -41,31 +41,6 @@
* Prefilled mixins * Prefilled mixins
* Mixins with fixed values * Mixins with fixed values
*/ */
@mixin bg-light-gray-gradient {
background: #f1f1f1;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -ms-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
}
@mixin bg-gray-gradient {
background: #eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
@mixin bg-dark-gray-gradient {
background: #eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -ms-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
}
@mixin shade { @mixin shade {
@include box-shadow(0 0 3px #ddd); @include box-shadow(0 0 3px #ddd);
...@@ -77,7 +52,6 @@ ...@@ -77,7 +52,6 @@
@mixin header-font { @mixin header-font {
color: $style_color; color: $style_color;
text-shadow: 0 1px 1px #FFF;
font-size: 16px; font-size: 16px;
line-height: 44px; line-height: 44px;
font-weight: normal; font-weight: normal;
......
...@@ -8,6 +8,31 @@ $bg_style_color: #2299BB; ...@@ -8,6 +8,31 @@ $bg_style_color: #2299BB;
$list-group-active-bg: $bg_style_color; $list-group-active-bg: $bg_style_color;
$hover: #D9EDF7; $hover: #D9EDF7;
/*
* Success colors (green)
*/
$border_success: #4cae4c;
$bg_success: #5cb85c;
$bg_light_success: #dff0d8;
$color_success: #3c763d;
/*
* Danger colors (red)
*/
$border_danger: #d43f3a;
$bg_danger: #d9534f;
$bg_light_danger: #f2dede;
$color_danger: #a94442;
/*
* Primary colors (blue)
*/
$border_primary: #358ebd;
$bg_primary: #429bca;
$bg_light_primary: #d9edf7;
$color_primary: #31708f;
/** /**
* Commit Diff Colors * Commit Diff Colors
*/ */
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.diff-header { .diff-header {
@extend .clearfix; @extend .clearfix;
background: #DDD; background: #EEE;
border-bottom: 1px solid #CCC; border-bottom: 1px solid #CCC;
padding: 5px 5px 5px 10px; padding: 5px 5px 5px 10px;
color: #555; color: #555;
...@@ -63,30 +63,21 @@ ...@@ -63,30 +63,21 @@
} }
} }
.text-file-parallel div { tr.line_holder.parallel{
display: inline-block; .old_line, .new_line, .diff_line {
padding-bottom: 16px; min-width: 50px;
} }
.diff-side {
overflow-x: scroll; td.line_content.parallel{
width: 508px; width: 50%;
} }
.diff-side.diff-side-left{
overflow-y:hidden;
}
.diff-side table, td.diff-middle table {
}
.diff-middle {
width: 114px;
vertical-align: top;
overflow: hidden
} }
.old_line, .new_line, .diff_line { .old_line, .new_line, .diff_line {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
border: none; border: none;
background: #EEE; background: #F5F5F5;
color: #666; color: #666;
padding: 0px 5px; padding: 0px 5px;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
...@@ -304,15 +295,9 @@ ...@@ -304,15 +295,9 @@
} //.view.onion-skin } //.view.onion-skin
} }
.view-modes{ .view-modes{
padding: 10px; padding: 10px;
text-align: center; text-align: center;
background: #EEE;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
ul, li{ ul, li{
list-style: none; list-style: none;
......
.project-network { .project-network {
border: 1px solid #aaa; border: 1px solid #CCC;
padding: 1px;
.tip { .tip {
color: #888; color: #888;
font-size: 14px; font-size: 14px;
padding: 10px; padding: 10px;
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
@include bg-gray-gradient; background: #EEE;
} }
.network-graph { .network-graph {
background: #f1f1f1; background: #FFF;
height: 500px; height: 500px;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
......
...@@ -14,7 +14,6 @@ header { ...@@ -14,7 +14,6 @@ header {
.nav > li > a { .nav > li > a {
color: $style_color; color: $style_color;
text-shadow: 0 1px 0 #fff;
font-size: 14px; font-size: 14px;
line-height: 32px; line-height: 32px;
padding: 6px 10px; padding: 6px 10px;
...@@ -190,7 +189,6 @@ header { ...@@ -190,7 +189,6 @@ header {
.nav > li > a { .nav > li > a {
color: #AAA; color: #AAA;
text-shadow: 0 1px 0 #444;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
background: none; background: none;
...@@ -224,7 +222,6 @@ header { ...@@ -224,7 +222,6 @@ header {
background: image-url('logo-white.png') no-repeat center center; background: image-url('logo-white.png') no-repeat center center;
background-size: 32px; background-size: 32px;
color: #fff; color: #fff;
text-shadow: 0 1px 1px #444;
} }
} }
} }
...@@ -236,7 +233,6 @@ header { ...@@ -236,7 +233,6 @@ header {
} }
} }
color: #fff; color: #fff;
text-shadow: 0 1px 1px #444;
} }
} }
......
...@@ -45,14 +45,6 @@ ...@@ -45,14 +45,6 @@
padding: 6px 10px; padding: 6px 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
@include border-radius(4px); @include border-radius(4px);
input.check_all_issues {
padding: 0;
margin: 0;
position: relative;
top: 3px;
}
} }
.issues_content { .issues_content {
...@@ -143,3 +135,36 @@ form.edit-issue { ...@@ -143,3 +135,36 @@ form.edit-issue {
border-color: #E5E5E5; border-color: #E5E5E5;
} }
} }
@media (max-width: $screen-xs-max) {
.issue-btn-group {
width: 100%;
margin-top: 5px;
.btn-group {
width: 100%;
ul {
width: 100%;
text-align: center;
}
}
.btn {
width: 100%;
margin-top: -1px;
&:first-child:not(:last-child) {
border-radius: 4px 4px 0 0;
}
&:not(:first-child):not(:last-child) {
border-radius: 0;
}
&:last-child:not(:first-child) {
border-radius: 0 0 4px 4px;
}
}
}
}
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
.mr_source_commit, .mr_source_commit,
.mr_target_commit { .mr_target_commit {
margin-top: 10px;
.commit { .commit {
margin: 0; margin: 0;
padding: 2px 0; padding: 2px 0;
...@@ -74,6 +73,10 @@ ...@@ -74,6 +73,10 @@
.merge-request-info { .merge-request-info {
color: #999; color: #999;
.merge-request-labels {
display: inline-block;
}
} }
} }
} }
...@@ -112,3 +115,7 @@ ...@@ -112,3 +115,7 @@
} }
} }
} }
.merge-request-show-labels .label {
padding: 6px 10px;
}
...@@ -139,6 +139,7 @@ ul.notes { ...@@ -139,6 +139,7 @@ ul.notes {
background-color: #fff; background-color: #fff;
border-width: 1px 0; border-width: 1px 0;
padding-top: 0; padding-top: 0;
vertical-align: top;
li { li {
padding: 5px; padding: 5px;
......
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
} }
&.modern { &.modern {
background: #345; background: #009871;
} }
&.gray { &.gray {
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
} }
&.violet { &.violet {
background: #547; background: #548;
} }
} }
} }
......
...@@ -151,3 +151,5 @@ ...@@ -151,3 +151,5 @@
} }
} }
} }
#modal-remove-blob > .modal-dialog { width: 850px; }
...@@ -40,4 +40,10 @@ ...@@ -40,4 +40,10 @@
.votes-holder { .votes-holder {
float: right; float: right;
width: 250px; width: 250px;
@media (max-width: $screen-xs-max) {
width: 100%;
margin-top: 5px;
margin-bottom: 10px;
}
} }
...@@ -16,28 +16,28 @@ ...@@ -16,28 +16,28 @@
@extend .header-dark; @extend .header-dark;
&.navbar-gitlab { &.navbar-gitlab {
.navbar-inner { .navbar-inner {
background: #547; background: #548;
border-bottom: 1px solid #435; border-bottom: 1px solid #436;
.app_logo, .navbar-toggle { .app_logo, .navbar-toggle {
&:hover { &:hover {
background-color: #435; background-color: #436;
} }
} }
.separator { .separator {
background: #435; background: #436;
border-left: 1px solid #658; border-left: 1px solid #659;
} }
.nav > li > a { .nav > li > a {
color: #98B; color: #98C;
} }
.search-input { .search-input {
border-color: #98B; border-color: #98C;
} }
} }
} }
} }
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
background: #769; background: #659;
} }
} }
...@@ -16,24 +16,28 @@ ...@@ -16,24 +16,28 @@
@extend .header-dark; @extend .header-dark;
&.navbar-gitlab { &.navbar-gitlab {
.navbar-inner { .navbar-inner {
background: #345; background: #00AC7E;
border-bottom: 1px solid #234; border-bottom: 1px solid #00AC7E;
.app_logo, .navbar-toggle { .app_logo, .navbar-toggle {
&:hover { &:hover {
background-color: #234; background-color: #009C6E;
} }
} }
.separator { .separator {
background: #234; background: #009C6F;
border-left: 1px solid #456; border-left: 1px solid #10BC8E;
} }
.nav > li > a { .nav > li > a {
color: #89A; color: #ADC;
} }
.search-input { .search-input {
border-color: #89A; border-color: #7fd5be;
} }
} }
} }
} }
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
background: #00AC7E;
}
} }
...@@ -117,6 +117,11 @@ class ApplicationController < ActionController::Base ...@@ -117,6 +117,11 @@ class ApplicationController < ActionController::Base
return access_denied! unless can?(current_user, :push_code, project) return access_denied! unless can?(current_user, :push_code, project)
end end
def authorize_labels!
# Labels should be accessible for issues and/or merge requests
authorize_read_issue! || authorize_read_merge_request!
end
def access_denied! def access_denied!
render "errors/access_denied", layout: "errors", status: 404 render "errors/access_denied", layout: "errors", status: 404
end end
......
...@@ -68,7 +68,7 @@ class GroupsController < ApplicationController ...@@ -68,7 +68,7 @@ class GroupsController < ApplicationController
@members = group.users_groups @members = group.users_groups
if params[:search].present? if params[:search].present?
users = group.users.search(params[:search]) users = group.users.search(params[:search]).to_a
@members = @members.where(user_id: users) @members = @members.where(user_id: users)
end end
......
class Projects::LabelsController < Projects::ApplicationController class Projects::LabelsController < Projects::ApplicationController
before_filter :module_enabled before_filter :module_enabled
# Allow read any issue before_filter :authorize_labels!
before_filter :authorize_read_issue!
respond_to :js, :html respond_to :js, :html
...@@ -13,12 +12,18 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -13,12 +12,18 @@ class Projects::LabelsController < Projects::ApplicationController
def generate def generate
Gitlab::IssuesLabels.generate(@project) Gitlab::IssuesLabels.generate(@project)
redirect_to project_issues_path(@project) if params[:redirect] == 'issues'
redirect_to project_issues_path(@project)
elsif params[:redirect] == 'merge_requests'
redirect_to project_merge_requests_path(@project)
end
end end
protected protected
def module_enabled def module_enabled
return render_404 unless @project.issues_enabled unless @project.issues_enabled || @project.merge_requests_enabled
return render_404
end
end end
end end
...@@ -62,11 +62,27 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -62,11 +62,27 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.source_project = @project unless @merge_request.source_project @merge_request.source_project = @project unless @merge_request.source_project
@merge_request.target_project ||= (@project.forked_from_project || @project) @merge_request.target_project ||= (@project.forked_from_project || @project)
@target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
@merge_request.target_branch ||= @merge_request.target_project.default_branch @merge_request.target_branch ||= @merge_request.target_project.default_branch
@source_project = @merge_request.source_project @source_project = @merge_request.source_project
@merge_request
if @merge_request.target_branch && @merge_request.source_branch
compare_action = Gitlab::Satellite::CompareAction.new(
current_user,
@merge_request.target_project,
@merge_request.target_branch,
@merge_request.source_project,
@merge_request.source_branch
)
@commits = compare_action.commits
@commits.map! { |commit| Commit.new(commit) }
@commit = @commits.first
@diffs = compare_action.diffs
@merge_request.title = @merge_request.source_branch.titleize.humanize
@target_project = @merge_request.target_project
@target_repo = @target_project.repository
end
end end
def edit def edit
...@@ -80,7 +96,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -80,7 +96,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute @merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute
if @merge_request.valid? if @merge_request.valid?
redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.' redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.'
else else
@source_project = @merge_request.source_project @source_project = @merge_request.source_project
@target_project = @merge_request.target_project @target_project = @merge_request.target_project
......
...@@ -12,9 +12,22 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -12,9 +12,22 @@ class Projects::WikisController < Projects::ApplicationController
def show def show
@page = @project_wiki.find_page(params[:id], params[:version_id]) @page = @project_wiki.find_page(params[:id], params[:version_id])
gollum_wiki = @project_wiki.wiki
file = gollum_wiki.file(params[:id], gollum_wiki.ref, true)
if @page if @page
render 'show' render 'show'
elsif file
if file.on_disk?
send_file file.on_disk_path, disposition: 'inline'
else
send_data(
file.raw_data,
type: file.mime_type,
disposition: 'inline',
filename: file.name
)
end
else else
return render('empty') unless can?(current_user, :write_wiki, @project) return render('empty') unless can?(current_user, :write_wiki, @project)
@page = WikiPage.new(@project_wiki) @page = WikiPage.new(@project_wiki)
......
...@@ -117,7 +117,7 @@ module CommitsHelper ...@@ -117,7 +117,7 @@ module CommitsHelper
added_lines[line_new] = { line_code: line_code, type: type, line: line } added_lines[line_new] = { line_code: line_code, type: type, line: line }
end end
end end
max_length = old_file ? old_file.sloc + added_lines.length : file.sloc max_length = old_file ? [old_file.loc, file.loc].max : file.loc
offset1 = 0 offset1 = 0
offset2 = 0 offset2 = 0
......
...@@ -82,7 +82,7 @@ module IssuesHelper ...@@ -82,7 +82,7 @@ module IssuesHelper
end end
def milestone_options object def milestone_options object
options_from_collection_for_select(@project.milestones.active, 'id', 'title', object.milestone_id) options_from_collection_for_select(object.project.milestones.active, 'id', 'title', object.milestone_id)
end end
def issue_box_class(item) def issue_box_class(item)
......
...@@ -14,7 +14,7 @@ module SelectsHelper ...@@ -14,7 +14,7 @@ module SelectsHelper
css_class << (opts[:class] || '') css_class << (opts[:class] || '')
value = opts[:selected] || '' value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Select user' placeholder = opts[:placeholder] || 'Select user'
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder) hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end end
end end
...@@ -4,6 +4,7 @@ module Emails ...@@ -4,6 +4,7 @@ module Emails
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
@project = @issue.project @project = @issue.project
@target_url = project_issue_url(@project, @issue) @target_url = project_issue_url(@project, @issue)
set_message_id("issue_#{issue_id}")
mail(from: sender(@issue.author_id), mail(from: sender(@issue.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})")) subject: subject("#{@issue.title} (##{@issue.iid})"))
...@@ -14,6 +15,7 @@ module Emails ...@@ -14,6 +15,7 @@ module Emails
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @issue.project @project = @issue.project
@target_url = project_issue_url(@project, @issue) @target_url = project_issue_url(@project, @issue)
set_reference("issue_#{issue_id}")
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})")) subject: subject("#{@issue.title} (##{@issue.iid})"))
...@@ -24,6 +26,7 @@ module Emails ...@@ -24,6 +26,7 @@ module Emails
@project = @issue.project @project = @issue.project
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
@target_url = project_issue_url(@project, @issue) @target_url = project_issue_url(@project, @issue)
set_reference("issue_#{issue_id}")
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})")) subject: subject("#{@issue.title} (##{@issue.iid})"))
...@@ -35,6 +38,7 @@ module Emails ...@@ -35,6 +38,7 @@ module Emails
@project = @issue.project @project = @issue.project
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
@target_url = project_issue_url(@project, @issue) @target_url = project_issue_url(@project, @issue)
set_reference("issue_#{issue_id}")
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})")) subject: subject("#{@issue.title} (##{@issue.iid})"))
......
...@@ -4,9 +4,10 @@ module Emails ...@@ -4,9 +4,10 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project @project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
set_message_id("merge_request_#{merge_request_id}")
mail(from: sender(@merge_request.author_id), mail(from: sender(@merge_request.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id) def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
...@@ -14,9 +15,10 @@ module Emails ...@@ -14,9 +15,10 @@ module Emails
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @merge_request.project @project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
...@@ -24,18 +26,20 @@ module Emails ...@@ -24,18 +26,20 @@ module Emails
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
@project = @merge_request.project @project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project @project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
end end
......
...@@ -15,6 +15,7 @@ module Emails ...@@ -15,6 +15,7 @@ module Emails
@issue = @note.noteable @issue = @note.noteable
@project = @note.project @project = @note.project
@target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}") @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}")
set_reference("issue_#{@issue.id}")
mail(from: sender(@note.author_id), mail(from: sender(@note.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})")) subject: subject("#{@issue.title} (##{@issue.iid})"))
...@@ -25,9 +26,10 @@ module Emails ...@@ -25,9 +26,10 @@ module Emails
@merge_request = @note.noteable @merge_request = @note.noteable
@project = @note.project @project = @note.project
@target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}")
set_reference("merge_request_#{@merge_request.id}")
mail(from: sender(@note.author_id), mail(from: sender(@note.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def note_wall_email(recipient_id, note_id) def note_wall_email(recipient_id, note_id)
......
...@@ -53,6 +53,22 @@ class Notify < ActionMailer::Base ...@@ -53,6 +53,22 @@ class Notify < ActionMailer::Base
end end
end end
# Set the Message-ID header field
#
# local_part - The local part of the message ID
#
def set_message_id(local_part)
headers["Message-ID"] = "<#{local_part}@#{Gitlab.config.gitlab.host}>"
end
# Set the References header field
#
# local_part - The local part of the referenced message ID
#
def set_reference(local_part)
headers["References"] = "<#{local_part}@#{Gitlab.config.gitlab.host}>"
end
# Formats arguments into a String suitable for use as an email subject # Formats arguments into a String suitable for use as an email subject
# #
# extra - Extra Strings to be inserted into the subject # extra - Extra Strings to be inserted into the subject
......
...@@ -36,7 +36,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -36,7 +36,9 @@ class MergeRequest < ActiveRecord::Base
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description attr_accessible :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
:state_event, :description, :label_list
attr_accessor :should_remove_source_branch attr_accessor :should_remove_source_branch
...@@ -44,6 +46,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -44,6 +46,9 @@ class MergeRequest < ActiveRecord::Base
# It allows us to close or modify broken merge requests # It allows us to close or modify broken merge requests
attr_accessor :allow_broken attr_accessor :allow_broken
ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :labels
state_machine :state, initial: :opened do state_machine :state, initial: :opened do
event :close do event :close do
transition [:reopened, :opened] => :closed transition [:reopened, :opened] => :closed
...@@ -253,6 +258,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -253,6 +258,14 @@ class MergeRequest < ActiveRecord::Base
end end
end end
def target_project_namespace
if target_project && target_project.namespace
target_project.namespace.path
else
"(removed)"
end
end
def source_branch_exists? def source_branch_exists?
return false unless self.source_project return false unless self.source_project
......
...@@ -86,7 +86,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -86,7 +86,7 @@ class MergeRequestDiff < ActiveRecord::Base
# between target and source branches # between target and source branches
def unmerged_commits def unmerged_commits
commits = if merge_request.for_fork? commits = if merge_request.for_fork?
Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between compare_action.commits
else else
repository.commits_between(target_branch, source_branch) repository.commits_between(target_branch, source_branch)
end end
...@@ -150,7 +150,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -150,7 +150,7 @@ class MergeRequestDiff < ActiveRecord::Base
# between target and source branches # between target and source branches
def unmerged_diffs def unmerged_diffs
diffs = if merge_request.for_fork? diffs = if merge_request.for_fork?
Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite compare_action.diffs
else else
Gitlab::Git::Diff.between(repository, source_branch, target_branch) Gitlab::Git::Diff.between(repository, source_branch, target_branch)
end end
...@@ -165,4 +165,16 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -165,4 +165,16 @@ class MergeRequestDiff < ActiveRecord::Base
def repository def repository
merge_request.target_project.repository merge_request.target_project.repository
end end
private
def compare_action
Gitlab::Satellite::CompareAction.new(
merge_request.author,
merge_request.target_project,
merge_request.target_branch,
merge_request.source_project,
merge_request.source_branch
)
end
end end
...@@ -281,8 +281,11 @@ class Project < ActiveRecord::Base ...@@ -281,8 +281,11 @@ class Project < ActiveRecord::Base
self.id self.id
end end
# Tags are shared by issues and merge requests
def issues_labels def issues_labels
@issues_labels ||= (issues_default_labels + issues.tags_on(:labels)).uniq.sort_by(&:name) @issues_labels ||= (issues_default_labels +
merge_requests.tags_on(:labels) +
issues.tags_on(:labels)).uniq.sort_by(&:name)
end end
def issue_exists?(issue_id) def issue_exists?(issue_id)
......
...@@ -64,7 +64,8 @@ class ProjectWiki ...@@ -64,7 +64,8 @@ class ProjectWiki
# #
# Returns an initialized WikiPage instance or nil # Returns an initialized WikiPage instance or nil
def find_page(title, version = nil) def find_page(title, version = nil)
if page = wiki.page(title, version) page_title, page_dir = page_title_and_dir(title)
if page = wiki.page(page_title, version, page_dir)
WikiPage.new(self, page, true) WikiPage.new(self, page, true)
else else
nil nil
...@@ -90,6 +91,12 @@ class ProjectWiki ...@@ -90,6 +91,12 @@ class ProjectWiki
wiki.delete_page(page, commit_details(:deleted, message, page.title)) wiki.delete_page(page, commit_details(:deleted, message, page.title))
end end
def page_title_and_dir(title)
title_array = title.split("/")
title = title_array.pop
[title.gsub(/\.[^.]*$/, ""), title_array.join("/")]
end
private private
def create_repo! def create_repo!
......
...@@ -175,14 +175,24 @@ class WikiPage ...@@ -175,14 +175,24 @@ class WikiPage
end end
def save(method, *args) def save(method, *args)
if valid? && wiki.send(method, *args) project_wiki = wiki
@page = wiki.wiki.paged(title) if valid? && project_wiki.send(method, *args)
page_details = if method == :update_page
@page.path
else
title
end
page_title, page_dir = project_wiki.page_title_and_dir(page_details)
gollum_wiki = project_wiki.wiki
@page = gollum_wiki.paged(page_title, page_dir)
set_attributes set_attributes
@persisted = true @persisted = true
else else
errors.add(:base, wiki.error_message) if wiki.error_message errors.add(:base, project_wiki.error_message) if project_wiki.error_message
@persisted = false @persisted = false
end end
@persisted @persisted
......
...@@ -31,7 +31,8 @@ class SystemHooksService ...@@ -31,7 +31,8 @@ class SystemHooksService
path_with_namespace: model.path_with_namespace, path_with_namespace: model.path_with_namespace,
project_id: model.id, project_id: model.id,
owner_name: owner.name, owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : nil owner_email: owner.respond_to?(:email) ? owner.email : nil,
project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
}) })
when User when User
data.merge!({ data.merge!({
...@@ -46,7 +47,8 @@ class SystemHooksService ...@@ -46,7 +47,8 @@ class SystemHooksService
project_id: model.project_id, project_id: model.project_id,
user_name: model.user.name, user_name: model.user.name,
user_email: model.user.email, user_email: model.user.email,
project_access: model.human_access project_access: model.human_access,
project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
}) })
end end
end end
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
= form_for [:admin, @user], html: { class: 'form-horizontal' } do |f| = form_for [:admin, @user], html: { class: 'form-horizontal' } do |f|
-if @user.errors.any? -if @user.errors.any?
#error_explanation #error_explanation
%ul.unstyled.alert.alert-danger .alert.alert-danger
- @user.errors.full_messages.each do |msg| - @user.errors.full_messages.each do |msg|
%li= msg %p= msg
%fieldset %fieldset
%legend Account %legend Account
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
- note = event.target - note = event.target
- if note.attachment.url - if note.attachment.url
- if note.attachment.image? - if note.attachment.image?
= link_to note.attachment.url, target: '_blank' do = link_to note.attachment.secure_url, target: '_blank' do
= image_tag note.attachment.url, class: 'note-image-attach' = image_tag note.attachment.secure_url, class: 'note-image-attach'
- else - else
= link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
%i.icon-paper-clip %i.icon-paper-clip
......
.dashboard .dashboard
.activities.col-md-8.hidden-sm .activities.col-md-8.hidden-sm.hidden-xs
- if current_user - if current_user
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
= link_to dashboard_path, class: 'btn btn-tiny' do = link_to dashboard_path, class: 'btn btn-tiny' do
......
...@@ -43,6 +43,6 @@ ...@@ -43,6 +43,6 @@
%li %li
= link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
%i.icon-signout %i.icon-signout
%li %li.hidden-xs
= link_to current_user, class: "profile-pic", id: 'profile-pic' do = link_to current_user, class: "profile-pic", id: 'profile-pic' do
= image_tag avatar_icon(current_user.email, 26), alt: 'User activity' = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
%p %p
= "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}" = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
%p %p
= "Merge Request !#{@merge_request.iid} was merged" = "Merge Request ##{@merge_request.iid} was merged"
= "Merge Request #{@merge_request.iid} was merged" = "Merge Request ##{@merge_request.iid} was merged"
Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
...@@ -15,18 +15,18 @@ ...@@ -15,18 +15,18 @@
- else - else
= link_to title, '#' = link_to title, '#'
%ul.blob-commit-info.bs-callout.bs-callout-info %ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs
- blob_commit = @repository.last_commit_for_path(@commit.id, @blob.path) - blob_commit = @repository.last_commit_for_path(@commit.id, @blob.path)
= render blob_commit, project: @project = render blob_commit, project: @project
%div#tree-content-holder.tree-content-holder %div#tree-content-holder.tree-content-holder
.file-holder .file-holder
.file-title .file-title.clearfix
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= blob.name = blob.name
%small= number_to_human_size blob.size %small= number_to_human_size blob.size
%span.options= render "actions" %span.options.hidden-xs= render "actions"
- if blob.text? - if blob.text?
= render "text", blob: blob = render "text", blob: blob
- elsif blob.image? - elsif blob.image?
......
...@@ -14,7 +14,8 @@ ...@@ -14,7 +14,8 @@
= label_tag 'commit_message', class: "control-label" do = label_tag 'commit_message', class: "control-label" do
Commit message Commit message
.col-sm-10 .col-sm-10
= text_area_tag 'commit_message', params[:commit_message], placeholder: "Removed this file because...", required: true, rows: 3, class: 'form-control' = render 'shared/commit_message_container', {textarea: text_area_tag('commit_message',
params[:commit_message], placeholder: "Removed this file because...", required: true, rows: 3, class: 'form-control')}
.form-group .form-group
.col-sm-2 .col-sm-2
.col-sm-10 .col-sm-10
......
...@@ -2,54 +2,37 @@ ...@@ -2,54 +2,37 @@
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) - old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length - num_lines = old_lines.length
%div.text-file-parallel %div.text-file
%div.diff-side.diff-side-left %table
%table - num_lines.times do |index|
- old_lines.each do |line| - new_line = new_lines[index]
- old_line = old_lines[index]
%tr.line_holder.parallel
-# For old line
- if old_line.type == :file_created
%td.old_line= old_line.num
%td.line_content.parallel= "File was created"
- elsif old_line.type == :deleted
%td.old_line.old= old_line.num
%td.line_content{class: "parallel noteable_line old #{old_line.code}", "line_code" => old_line.code}= old_line.content
- else old_line.type == :no_change
%td.old_line= old_line.num
%td.line_content.parallel= old_line.content
-# For new line
- if new_line.type == :file_deleted
%td.new_line= new_line.num
%td.line_content.parallel= "File was deleted"
- elsif new_line.type == :added
%td.new_line.new= new_line.num
%td.line_content{class: "parallel noteable_line new #{new_line.code}", "line_code" => new_line.code}= new_line.content
- else new_line.type == :no_change
%td.new_line= new_line.num
%td.line_content.parallel= new_line.content
- if @reply_allowed
- comments1 = @line_notes.select { |n| n.line_code == old_line.code }.sort_by(&:created_at)
- comments2 = @line_notes.select { |n| n.line_code == new_line.code }.sort_by(&:created_at)
- unless comments1.empty? and comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2
%tr.line_holder.parallel
- if line.type == :file_created
%td.line_content.parallel= "File was created"
- elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%div.diff-middle
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line=""
- if new_lines[index].type == :added
%td.new_line.new= new_lines[index].num
- else
%td.new_line= new_lines[index].num
%div.diff-side.diff-side-right
%table
- new_lines.each do |line|
%tr.line_holder.parallel
- if line.type == :file_deleted
%td.line_content.parallel= "File was deleted"
- elsif line.type == :added
%td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
:javascript
$('.diff-side-right').on('scroll', function(){
$('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
$('.diff-side-left').scrollLeft($(this).scrollLeft());
});
$('.diff-side-left').on('scroll', function(){
$('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
$('.diff-side-right').scrollLeft($(this).scrollLeft());
});
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
= label_tag 'commit_message', class: "control-label" do = label_tag 'commit_message', class: "control-label" do
Commit message Commit message
.col-sm-10 .col-sm-10
= text_area_tag 'commit_message', '', placeholder: "Update #{@blob.name}", required: true, rows: 3, class: 'form-control' = render 'shared/commit_message_container', {textarea: text_area_tag('commit_message', '',
placeholder: "Update #{@blob.name}", required: true, rows: 3, class: 'form-control')}
.form-actions .form-actions
= hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'content', '', id: "file-content"
......
= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| = form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
%strong.append-right-10 .row
Assignee: .col-md-6
%strong.append-right-10
Assignee:
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @issue.assignee_id) = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @issue.assignee_id)
- elsif issue.assignee - elsif issue.assignee
= link_to_member(@project, @issue.assignee) = link_to_member(@project, @issue.assignee)
- else - else
None None
.pull-right .col-md-6.text-right
%strong.append-right-10 %strong.append-right-10
Milestone: Milestone:
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'}) = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
= hidden_field_tag :issue_context = hidden_field_tag :issue_context
= f.submit class: 'btn' = f.submit class: 'btn'
- elsif issue.milestone - elsif issue.milestone
= link_to issue.milestone.title, project_milestone_path = link_to issue.milestone.title, project_milestone_path
- else - else
None None
= render "head" = render "head"
.row .row
.col-md-3 .col-md-3
= render 'shared/project_filter', project_entities_path: project_issues_path(@project), labels: true = render 'shared/project_filter', project_entities_path: project_issues_path(@project),
labels: true, redirect: 'issues'
.col-md-9.issues-holder .col-md-9.issues-holder
= render "issues" = render "issues"
%h3.page-title %h3.page-title
Issue ##{@issue.iid} Issue ##{@issue.iid}
%span.pull-right %span.pull-right.issue-btn-group
- if can?(current_user, :write_issue, @project) - if can?(current_user, :write_issue, @project)
= link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do = link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do
%i.icon-plus %i.icon-plus
...@@ -16,28 +16,29 @@ ...@@ -16,28 +16,29 @@
%i.icon-edit %i.icon-edit
Edit Edit
.votes-holder .clearfix
#votes= render 'votes/votes_block', votable: @issue .votes-holder
#votes= render 'votes/votes_block', votable: @issue
.back-link .back-link
= link_to project_issues_path(@project) do = link_to project_issues_path(@project) do
&larr; To issues list &larr; To issues list
%span.milestone-nav-link %span.milestone-nav-link
- if @issue.milestone - if @issue.milestone
| |
%span.light Milestone %span.light Milestone
= link_to project_milestone_path(@project, @issue.milestone) do = link_to project_milestone_path(@project, @issue.milestone) do
= @issue.milestone.title = @issue.milestone.title
.issue-box{ class: issue_box_class(@issue) } .issue-box{ class: issue_box_class(@issue) }
.state .state.clearfix
%span.state-label .state-label.col-sm-2.col-xs-12
- if @issue.closed? - if @issue.closed?
Closed Closed
- else - else
Open Open
%span.creator %span.creator.col-sm-9.col-xs-12
Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)} Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)}
%h4.title %h4.title
......
...@@ -14,33 +14,6 @@ ...@@ -14,33 +14,6 @@
- @merge_request.errors.full_messages.each do |msg| - @merge_request.errors.full_messages.each do |msg|
%div= msg %div= msg
.merge-request-branches
.form-group
= label_tag nil, class: 'control-label' do
From
.col-sm-10
.clearfix
.pull-left
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.mr_source_commit
%br
.form-group
= label_tag nil, class: 'control-label' do
To
.col-sm-10
.clearfix
.pull-left
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.mr_target_commit
%hr
.merge-request-form-info .merge-request-form-info
.form-group .form-group
= f.label :title, class: 'control-label' do = f.label :title, class: 'control-label' do
...@@ -51,6 +24,32 @@ ...@@ -51,6 +24,32 @@
.col-sm-10 .col-sm-10
= f.text_area :description, class: "form-control js-gfm-input", rows: 14 = f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
%hr
.form-group
.issue-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
- if @merge_request.persisted? # Only allow labels on edit to avoid fork vs upstream repo labels issue
.form-group
= f.label :label_list, class: 'control-label' do
%i.icon-tag
Labels
.col-sm-10
= f.text_field :label_list, maxlength: 2000, class: "form-control"
%p.hint Separate labels with commas.
.form-actions .form-actions
- if @merge_request.new_record? - if @merge_request.new_record?
...@@ -66,20 +65,36 @@ ...@@ -66,20 +65,36 @@
:javascript :javascript
disableButtonIfEmptyField("#merge_request_title", ".btn-save"); disableButtonIfEmptyField("#merge_request_title", ".btn-save");
$('.assign-to-me-link').on('click', function(e){
var source_branch = $("#merge_request_source_branch") $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
, target_branch = $("#merge_request_target_branch") e.preventDefault();
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
});
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
}); });
$("#merge_request_label_list")
.bind( "keydown", function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB &&
$( this ).data( "autocomplete" ).menu.active ) {
event.preventDefault();
}
})
.bind("click", function(event) {
$(this).autocomplete("search", "");
})
.autocomplete({
minLength: 0,
source: function( request, response ) {
response( $.ui.autocomplete.filter(
#{raw labels_autocomplete_source}, extractLast( request.term ) ) );
},
focus: function() {
return false;
},
select: function(event, ui) {
var terms = split( this.value );
terms.pop();
terms.push( ui.item.value );
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
});
...@@ -11,13 +11,9 @@ ...@@ -11,13 +11,9 @@
- if merge_request.for_fork? - if merge_request.for_fork?
%span.light %span.light
#{merge_request.source_project_namespace}: #{merge_request.source_project_namespace}:
= merge_request.source_branch = truncate merge_request.source_branch, length: 25
%i.icon-angle-right.light %i.icon-angle-right.light
= merge_request.target_branch = merge_request.target_branch
- else
= merge_request.source_branch
%i.icon-angle-right.light
= merge_request.target_branch
.merge-request-info .merge-request-info
- if merge_request.author - if merge_request.author
authored by #{link_to_member(merge_request.source_project, merge_request.author)} authored by #{link_to_member(merge_request.source_project, merge_request.author)}
...@@ -35,3 +31,9 @@ ...@@ -35,3 +31,9 @@
.pull-right .pull-right
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')} %small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
.merge-request-labels
- merge_request.labels.each do |label|
%span{class: "label #{label_css_class(label.name)}"}
%i.icon-tag
= label.name
%h3.page-title Compare branches for new Merge Request
%hr
= form_for [@project, @merge_request], url: new_project_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline" } do |f|
.hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row
.col-md-6
.panel.panel-default
.panel-heading
%strong Source branch
.panel-body
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? })
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.panel-footer
.mr_source_commit
.col-md-6
.panel.panel-default
.panel-heading
%strong Target branch
.panel-body
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.panel-footer
.mr_target_commit
-if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
- if @merge_request.source_branch.present? && @merge_request.target_branch.present?
.light-well
%center
%h4
There isn't anything to merge.
%p.slead
- if @merge_request.source_branch == @merge_request.target_branch
You'll need to use different branch names to get a valid comparison.
- else
%span.label-branch #{@merge_request.source_branch}
and
%span.label-branch #{@merge_request.target_branch}
are the same.
%hr
= f.submit 'Compare branches', class: "btn btn-primary mr-compare-btn"
:javascript
var source_branch = $("#merge_request_source_branch")
, target_branch = $("#merge_request_target_branch")
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
:coffeescript
$(".merge-request-form").on 'submit', ->
if $("#merge_request_source_branch").val() is "" or $('#merge_request_target_branch').val() is ""
$(".mr-compare-errors").html("You must select source and target branch to proceed")
$(".mr-compare-errors").fadeIn()
event.preventDefault()
return
%h3.page-title
New merge request
%p.slead
From
%strong.monospace
#{@merge_request.source_project_namespace}:#{@merge_request.source_branch}
into
%strong.monospace
#{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
%span.pull-right
= link_to 'Change branches', new_project_merge_request_path(@project)
= form_for [@project, @merge_request], html: { class: "merge-request-form" } do |f|
.panel.panel-default
.panel-body
.form-group
.light
= f.label :title do
= "Title *"
= f.text_field :title, class: "form-control input-lg js-gfm-input", maxlength: 255, rows: 5, required: true
.form-group
.light
= f.label :description, "Description"
= f.text_area :description, class: "form-control js-gfm-input", rows: 10
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.form-group
.issue-assignee
= f.label :assignee_id do
%i.icon-user
Assign to
%div
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id do
%i.icon-time
Milestone
%div= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.panel-footer
- if @target_repo.contribution_guide
- contribution_guide_url = project_blob_path(@target_project, tree_join(@target_repo.root_ref, @target_repo.contribution_guide.name))
%p
Please review the
%strong #{link_to "guidelines for contribution", contribution_guide_url}
to this repository.
= f.hidden_field :source_project_id
= f.hidden_field :target_project_id
= f.hidden_field :target_branch
= f.hidden_field :source_branch
= f.submit 'Submit merge request', class: "btn btn-create"
.mr-compare
%div.ui-box
.title
Commits (#{@commits.count})
- if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
%ul.well-list
- Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE)).each do |commit|
= render "projects/commits/inline_commit", commit: commit, project: @project
%li.warning-row.unstyled
other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Changes
- if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line changes are not shown.
:javascript
$('.assign-to-me-link').on('click', function(e){
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
= render "projects/merge_requests/show/mr_box" = render "projects/merge_requests/show/mr_box"
= render "projects/merge_requests/show/state_widget" = render "projects/merge_requests/show/state_widget"
= render "projects/merge_requests/show/commits" = render "projects/merge_requests/show/commits"
= render "projects/merge_requests/show/participants"
- if @commits.present? - if @commits.present?
%ul.nav.nav-tabs %ul.nav.nav-tabs
......
:plain :plain
$(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}"); $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) {
mrTitle.val("#{params[:ref].titleize.humanize}");
}
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
%hr %hr
.row .row
.col-md-3 .col-md-3
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project) = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project),
labels: true, redirect: 'merge_requests'
.col-md-9 .col-md-9
.mr-filters.append-bottom-10 .mr-filters.append-bottom-10
.dropdown.inline .dropdown.inline
......
%h3.page-title New Merge Request - if @commits.present?
%hr = render 'new_submit'
= render 'form' - else
= render 'new_compare'
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| = form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
%strong.append-right-10 .row
Assignee: .col-md-6
%strong.append-right-10
Assignee:
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @merge_request.assignee_id) = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @merge_request.assignee_id)
- elsif merge_request.assignee - elsif merge_request.assignee
= link_to_member(@project, @merge_request.assignee) = link_to_member(@project, @merge_request.assignee)
- else - else
None None
.pull-right .col-md-6.text-right
%strong.append-right-10 %strong.append-right-10
Milestone: Milestone:
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'}) = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
= hidden_field_tag :merge_request_context = hidden_field_tag :merge_request_context
= f.submit class: 'btn' = f.submit class: 'btn'
- elsif merge_request.milestone - elsif merge_request.milestone
= link_to merge_request.milestone.title, project_milestone_path = link_to merge_request.milestone.title, project_milestone_path
- else - else
None None
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
- if @show_merge_controls - if @show_merge_controls
.automerge_widget.can_be_merged.hide .automerge_widget.can_be_merged.hide
.clearfix .clearfix
= form_for [:automerge, @project, @merge_request], remote: true, method: :get do |f| = form_for [:automerge, @project, @merge_request], remote: true, method: :post do |f|
%h4 %h4
You can accept this request automatically. You can accept this request automatically.
%div %div
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
= link_to "click here", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" = link_to "click here", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
for instructions. for instructions.
.js-toggle-container .js-toggle-container
%p %p
If you want to modify merge commit message - If you want to modify merge commit message -
...@@ -31,7 +30,8 @@ ...@@ -31,7 +30,8 @@
.form-group .form-group
= label_tag :merge_commit_message, "Commit message", class: 'control-label' = label_tag :merge_commit_message, "Commit message", class: 'control-label'
.col-sm-10 .col-sm-10
= text_area_tag :merge_commit_message, @merge_request.merge_commit_message, class: "form-control js-gfm-input", rows: 14, required: true = render 'shared/commit_message_container', {textarea: text_area_tag(:merge_commit_message,
@merge_request.merge_commit_message, class: "form-control js-gfm-input", rows: 14, required: true)}
%p.hint %p.hint
The recommended maximum line length is 52 characters for the first line and 72 characters for all following lines. The recommended maximum line length is 52 characters for the first line and 72 characters for all following lines.
......
.issue-box{ class: issue_box_class(@merge_request) } .issue-box{ class: issue_box_class(@merge_request) }
.state .state.clearfix
%span.state-label %span.state-label.col-sm-2.col-xs-12
- if @merge_request.merged? - if @merge_request.merged?
Merged Merged
- elsif @merge_request.closed? - elsif @merge_request.closed?
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
Open Open
%span.creator %span.creator.col-sm-9.col-xs-12
Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)} Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
%h4.title %h4.title
......
%h3.page-title %h3.page-title
= "Merge Request ##{@merge_request.iid}" = "Merge Request ##{@merge_request.iid}"
%span.pull-right %span.pull-right.issue-btn-group
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open? - if @merge_request.open?
.btn-group.pull-left .btn-group.pull-left
...@@ -39,4 +39,4 @@ ...@@ -39,4 +39,4 @@
- else - else
%span= @merge_request.source_branch %span= @merge_request.source_branch
&rarr; &rarr;
%spanh= @merge_request.target_branch %span= @merge_request.target_branch
.participants
%cite.cgray #{@merge_request.participants.count} participants
- @merge_request.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
.merge-request-show-labels.pull-right
- @merge_request.labels.each do |label|
%span{class: "label #{label_css_class(label.name)}"}
%i.icon-tag
= label.name
&nbsp;
...@@ -21,14 +21,6 @@ ...@@ -21,14 +21,6 @@
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)} #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
= render "projects/merge_requests/show/remove_source_branch" = render "projects/merge_requests/show/remove_source_branch"
- if !@closes_issues.empty? && @merge_request.open?
.alert.alert-info.alert-info
%span
%i.icon-ok
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
= succeed '.' do
!= gfm(@closes_issues.map { |i| "##{i.iid}" }.to_sentence)
- unless @commits.any? - unless @commits.any?
%h4 Nothing to merge %h4 Nothing to merge
%p %p
...@@ -38,3 +30,12 @@ ...@@ -38,3 +30,12 @@
%span.label-branch #{@merge_request.target_branch} %span.label-branch #{@merge_request.target_branch}
%br %br
Try to use different branches or push new code. Try to use different branches or push new code.
- if !@closes_issues.empty? && @merge_request.open?
.panel-footer
%span
%i.icon-ok
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
= succeed '.' do
!= gfm(@closes_issues.map { |i| "##{i.iid}" }.to_sentence)
= render "projects/issues/head" = render "projects/issues/head"
%h3.page-title %h3.page-title
Milestone ##{@milestone.iid} Milestone ##{@milestone.iid}
%small
= @milestone.expires_at
.pull-right .pull-right
- if can?(current_user, :admin_milestone, @project) - if can?(current_user, :admin_milestone, @project)
= link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped" do = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped" do
...@@ -23,14 +21,16 @@ ...@@ -23,14 +21,16 @@
.issue-box{ class: issue_box_class(@milestone) } .issue-box{ class: issue_box_class(@milestone) }
.state .state.clearfix
%span.state-label .state-label.col-sm-2.col-xs-12
- if @milestone.closed? - if @milestone.closed?
Closed Closed
- elsif @milestone.expired? - elsif @milestone.expired?
Expired Expired
- else - else
Open Open
%span.creator.col-sm-9.col-xs-12
= @milestone.expires_at
%h4.title %h4.title
= gfm escape_once(@milestone.title) = gfm escape_once(@milestone.title)
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
%ul.bordered-list %ul.bordered-list
- @users.each do |user| - @users.each do |user|
%li %li
= link_to user, title: user.name, class: "dark" do = link_to user, title: user.name, class: "darken" do
= image_tag avatar_icon(user.email, 32), class: "avatar s32" = image_tag avatar_icon(user.email, 32), class: "avatar s32"
%strong= truncate(user.name, lenght: 40) %strong= truncate(user.name, lenght: 40)
%br %br
......
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
= label_tag 'commit_message', class: "control-label" do = label_tag 'commit_message', class: "control-label" do
Commit message Commit message
.col-sm-10 .col-sm-10
= text_area_tag 'commit_message', params[:commit_message], placeholder: "Added new file", required: true, rows: 3, class: 'form-control' = render 'shared/commit_message_container', {textarea: text_area_tag('commit_message',
params[:commit_message], placeholder: "Added new file", required: true, rows: 3, class: 'form-control')}
.file-holder .file-holder
.file-title .file-title
......
- note1 = notes1.first # example note - note1 = notes1.first # example note
- note2 = notes2.first # example note - note2 = notes2.first # example note
-# Check if line want not changed since comment was left
/- if !defined?(line) || line == note.diff_line
%tr.notes_holder.js-toggle-content %tr.notes_holder.js-toggle-content
-# Check if line want not changed since comment was left
/- if !defined?(line1) || line1 == note1.diff_line
- if note1 - if note1
%td.notes_line
%span.btn.disabled
%i.icon-comment
= notes1.count
%td.notes_content %td.notes_content
%ul.notes{ rel: note1.discussion_id } %ul.notes{ rel: note1.discussion_id }
= render notes1 = render notes1
= render "projects/notes/discussion_reply_button", note: note1 = render "projects/notes/discussion_reply_button", note: note1
%td.notes_line2
%span.btn.disabled.parallel-comment
%i.icon-comment
= notes1.count
- else - else
%td= "" %td= ""
%td= "" %td= ""
%td= ""
-# Check if line want not changed since comment was left
/- if !defined?(line2) || line2 == note2.diff_line
- if note2 - if note2
%td.notes_line %td.notes_line
%span.btn.disabled.parallel-comment %span.btn.disabled
%i.icon-comment %i.icon-comment
= notes2.count = notes2.count
%td.notes_content %td.notes_content
%ul.notes{ rel: note2.discussion_id } %ul.notes{ rel: note2.discussion_id }
= render notes2 = render notes2
= render "projects/notes/discussion_reply_button", note: note2 = render "projects/notes/discussion_reply_button", note: note2
- else - else
%td= "" %td= ""
%td= "" %td= ""
...@@ -54,8 +54,8 @@ ...@@ -54,8 +54,8 @@
- if note.attachment.url - if note.attachment.url
.note-attachment .note-attachment
- if note.attachment.image? - if note.attachment.image?
= link_to note.attachment.url, target: '_blank' do = link_to note.attachment.secure_url, target: '_blank' do
= image_tag note.attachment.url, class: 'note-image-attach' = image_tag note.attachment.secure_url, class: 'note-image-attach'
.attachment.pull-right .attachment.pull-right
= link_to note.attachment.secure_url, target: "_blank" do = link_to note.attachment.secure_url, target: "_blank" do
%i.icon-paper-clip %i.icon-paper-clip
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
= render 'shared/event_filter' = render 'shared/event_filter'
.content_list .content_list
= spinner = spinner
.col-md-3.project-side.hidden-sm .col-md-3.project-side.hidden-sm.hidden-xs
.clearfix .clearfix
- if @project.archived? - if @project.archived?
.alert.alert-warning .alert.alert-warning
......
= form_for [@project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal' } do |f| = form_for [@project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal' } do |f|
-if @page.errors.any? -if @page.errors.any?
#error_explanation #error_explanation
%h2= "#{pluralize(@page.errors.count, "error")} prohibited this wiki from being saved:" .alert.alert-danger
%ul
- @page.errors.full_messages.each do |msg| - @page.errors.full_messages.each do |msg|
%li= msg %p= msg
= f.hidden_field :title, value: @page.title = f.hidden_field :title, value: @page.title
.form-group .form-group
......
...@@ -9,6 +9,6 @@ ...@@ -9,6 +9,6 @@
%span Page slug %span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project) = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project)
%p.hint %p.hint
Please don't use spaces and slashes Please don't use spaces.
.modal-footer .modal-footer
= link_to 'Build', '#', class: 'build-new-wiki btn btn-create' = link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
.commit-message-container
.max-width-marker
-# When the `ch` CSS length unit becomes widely supported `http://www.quirksmode.org/css/units-values` remove this workaround.
= 'a' * 72
= textarea
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
.light-well .light-well
Add first label to your issues Add first label to your issues
%br %br
or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels or #{link_to 'generate', generate_project_labels_path(@project, redirect: redirect), method: :post} default set of labels
%fieldset %fieldset
- if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any? - if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any?
......
#!/bin/sh
set -e
for file in config/*.yml.example; do
cp ${file} config/$(basename ${file} .example)
done
# Allow to override the Gitlab URL from an environment variable, as this will avoid having to change the configuration file for simple deployments.
config=$(echo '<% gitlab_url = URI(ENV["GITLAB_URL"] || "http://localhost:80") %>' | cat - config/gitlab.yml)
echo "$config" > config/gitlab.yml
sed -i "s/host: localhost/host: <%= gitlab_url.host %>/" config/gitlab.yml
sed -i "s/port: 80/port: <%= gitlab_url.port %>/" config/gitlab.yml
sed -i "s/https: false/https: <%= gitlab_url.scheme == 'https' %>/" config/gitlab.yml
# No need for config file. Will be taken care of by REDIS_URL env variable
rm config/resque.yml
# Set default unicorn.rb file
echo "" > config/unicorn.rb
# Required for assets precompilation
sudo service postgresql start
...@@ -66,13 +66,16 @@ module Gitlab ...@@ -66,13 +66,16 @@ module Gitlab
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.0'
# Relative url support
# Uncomment and customize the last line to run in a non-root path # Uncomment and customize the last line to run in a non-root path
# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
# Note that four settings need to be changed for this to work. # Note that following settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab" # 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab # 2) In your gitlab.yml file: relative_url_root: /gitlab
# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab" # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
# 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab" # 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
# 5) In lib/support/nginx/gitlab : do not use asset gzipping, remove block starting with "location ~ ^/(assets)/"
#
# To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production # To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
# #
# config.relative_url_root = "/gitlab" # config.relative_url_root = "/gitlab"
......
...@@ -24,15 +24,8 @@ production: &base ...@@ -24,15 +24,8 @@ production: &base
# Otherwise, ssh host will be set to the `host:` value above # Otherwise, ssh host will be set to the `host:` value above
# ssh_host: ssh.host_example.com # ssh_host: ssh.host_example.com
# Uncomment and customize the last line to run in a non-root path # WARNING: See config/application.rb under "Relative url support" for the list of
# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. # other files that need to be changed for relative url support
# Note that four settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
# 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
# To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
#
# relative_url_root: /gitlab # relative_url_root: /gitlab
# Uncomment and customize if you can't use the default user to run GitLab (default: 'git') # Uncomment and customize if you can't use the default user to run GitLab (default: 'git')
...@@ -40,10 +33,10 @@ production: &base ...@@ -40,10 +33,10 @@ production: &base
## Email settings ## Email settings
# Email address used in the "From" field in mails sent by GitLab # Email address used in the "From" field in mails sent by GitLab
email_from: gitlab@localhost email_from: example@example.com
# Email address of your support contact (default: same as email_from) # Email address of your support contact (default: same as email_from)
support_email: support@localhost support_email: support@example.com
## User settings ## User settings
default_projects_limit: 10 default_projects_limit: 10
......
...@@ -206,7 +206,7 @@ Gitlab::Application.routes.draw do ...@@ -206,7 +206,7 @@ Gitlab::Application.routes.draw do
end end
end end
resources :wikis, only: [:show, :edit, :destroy, :create], constraints: {id: /[a-zA-Z.0-9_\-]+/} do resources :wikis, only: [:show, :edit, :destroy, :create], constraints: {id: /[a-zA-Z.0-9_\-\/]+/} do
collection do collection do
get :pages get :pages
put ':id' => 'wikis#update' put ':id' => 'wikis#update'
...@@ -273,7 +273,7 @@ Gitlab::Application.routes.draw do ...@@ -273,7 +273,7 @@ Gitlab::Application.routes.draw do
resources :merge_requests, constraints: {id: /\d+/}, except: [:destroy] do resources :merge_requests, constraints: {id: /\d+/}, except: [:destroy] do
member do member do
get :diffs get :diffs
get :automerge post :automerge
get :automerge_check get :automerge_check
get :ci_status get :ci_status
end end
......
...@@ -8,14 +8,8 @@ ...@@ -8,14 +8,8 @@
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation. # documentation.
# Uncomment and customize the last line to run in a non-root path # WARNING: See config/application.rb under "Relative url support" for the list of
# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. # other files that need to be changed for relative url support
# Note that four settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
# 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
# To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
# #
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab" # ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
......
...@@ -40,7 +40,8 @@ Gitlab::Seeder.quiet do ...@@ -40,7 +40,8 @@ Gitlab::Seeder.quiet do
import_url: url, import_url: url,
namespace_id: group.id, namespace_id: group.id,
name: project_path.titleize, name: project_path.titleize,
description: Faker::Lorem.sentence description: Faker::Lorem.sentence,
visibility_level: Gitlab::VisibilityLevel.values.sample
} }
project = Projects::CreateService.new(User.first, params).execute project = Projects::CreateService.new(User.first, params).execute
......
Gitlab::Seeder.quiet do Gitlab::Seeder.quiet do
(1..100).each do |i| Project.all.reject(&:empty_repo?).each do |project|
# Random Project branches = project.repository.branch_names
project = Project.all.sample
branches.each do |branch_name|
# Random user break if branches.size < 2
user = project.team.users.sample source_branch = branches.pop
target_branch = branches.pop
next unless user
# Random user
next if project.empty_repo? user = project.team.users.sample
next unless user
branches = project.repository.branch_names.sample(2)
params = {
next if branches.uniq.size < 2 source_branch: source_branch,
target_branch: target_branch,
user_id = user.id title: Faker::Lorem.sentence(6),
description: Faker::Lorem.sentences(3).join(" ")
Gitlab::Seeder.by_user(user) do }
MergeRequest.seed(:id, [{
id: i, merge_request = MergeRequests::CreateService.new(project, user, params).execute
source_branch: branches.first,
target_branch: branches.last, if merge_request.valid?
source_project_id: project.id, merge_request.assignee = user
target_project_id: project.id, merge_request.milestone = project.milestones.sample
author_id: user_id, merge_request.save
assignee_id: user_id, print '.'
milestone: project.milestones.sample, else
title: Faker::Lorem.sentence(6) print 'F'
}]) end
end end
print('.')
end end
end end
MergeRequest.all.map do |mr|
mr.set_iid
mr.save
end
puts 'Load diffs for Merge Requests (it will take some time)...'
MergeRequest.all.each do |mr|
mr.reload_code
print '.'
end
...@@ -21,10 +21,12 @@ ...@@ -21,10 +21,12 @@
## Clients ## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP + [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Laravel API Wrapper for GitLab CE](https://github.com/adamgoose/gitlab) - PHP / [Laravel](http://laravel.com)
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby + [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python + [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java + [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
+ [node-gitlab](https://github.com/moul/node-gitlab) - Node.js + [node-gitlab](https://github.com/moul/node-gitlab) - Node.js
+ [NGitLab](https://github.com/Scooletz/NGitLab) - .NET
## Introduction ## Introduction
......
# Adding deploy keys to multiple projects
If you want to easily add the same deploy key to multiple projects in the same group, this can be achieved quite easily with the API.
First, find the ID of the projects you're interested in, by either listing all projects:
```
curl --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/projects
```
Or finding the id of a group and then listing all projects in that group:
```
curl --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/groups
# For group 1234:
curl --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/groups/1234
```
With those IDs, add the same deploy key to all:
```
for project_id in 321 456 987; do
curl -X POST --data '{"title": "my key", "key": "ssh-rsa AAAA..."}' --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/projects/${project_id}/keys
done
```
...@@ -189,6 +189,54 @@ Parameters: ...@@ -189,6 +189,54 @@ Parameters:
``` ```
## Accept MR
Merge changes submitted with MR usign this API.
If merge success you get 200 OK.
If it has some conflicts and can not be merged - you get 405 and error message 'Branch cannot be merged'
If merge request is already merged or closed - you get 405 and error message 'Method Not Allowed'
If you dont have permissions to accept this merge request - you get 401
```
PUT /projects/:id/merge_request/:merge_request_id/merge
```
Parameters:
+ `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of MR
+ `merge_commit_message` (optional) - Custom merge commit message
```json
{
"id": 1,
"target_branch": "master",
"source_branch": "test1",
"project_id": 3,
"title": "test1",
"state": "merged",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
"email": "admin@local.host",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@local.host",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
}
}
```
## Post comment to MR ## Post comment to MR
Adds a comment to a merge request. Adds a comment to a merge request.
......
...@@ -43,7 +43,8 @@ GET /projects ...@@ -43,7 +43,8 @@ GET /projects
"owner_id": 1, "owner_id": 1,
"path": "diaspora", "path": "diaspora",
"updated_at": "2013-09-30T13: 46: 02Z" "updated_at": "2013-09-30T13: 46: 02Z"
} },
"archived": false
}, },
{ {
"id": 6, "id": 6,
...@@ -78,7 +79,8 @@ GET /projects ...@@ -78,7 +79,8 @@ GET /projects
"owner_id": 1, "owner_id": 1,
"path": "brightbox", "path": "brightbox",
"updated_at": "2013-09-30T13:46:02Z" "updated_at": "2013-09-30T13:46:02Z"
} },
"archived": false
} }
] ]
``` ```
...@@ -157,7 +159,8 @@ Parameters: ...@@ -157,7 +159,8 @@ Parameters:
"access_level": 50, "access_level": 50,
"notification_level": 3 "notification_level": 3
} }
} },
"archived": false
} }
``` ```
......
# Select Version to Install # Select Version to Install
Make sure you view this installation guide from the branch (version) of GitLab you would like to install. In most cases Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install. In most cases
this should be the highest numbered stable branch (example shown below). this should be the highest numbered stable branch (example shown below).
![capture](http://i.imgur.com/d2AlIVj.png) ![capture](http://i.imgur.com/d2AlIVj.png)
If this is unclear check the [GitLab Blog](https://www.gitlab.com/blog/) for installation guide links by version. If the highest number stable branch is unclear please check the [GitLab Blog](https://www.gitlab.com/blog/) for installation guide links by version.
# Important notes # Important notes
...@@ -86,7 +86,7 @@ Is the system packaged Git too old? Remove it and compile from source. ...@@ -86,7 +86,7 @@ Is the system packaged Git too old? Remove it and compile from source.
mail server. By default, Debian is shipped with exim4 whereas Ubuntu mail server. By default, Debian is shipped with exim4 whereas Ubuntu
does not ship with one. The recommended mail server is postfix and you can install it with: does not ship with one. The recommended mail server is postfix and you can install it with:
sudo apt-get install -y postfix sudo apt-get install -y postfix
Then select 'Internet Site' and press enter to confirm the hostname. Then select 'Internet Site' and press enter to confirm the hostname.
...@@ -101,8 +101,8 @@ Remove the old Ruby 1.8 if present ...@@ -101,8 +101,8 @@ Remove the old Ruby 1.8 if present
Download Ruby and compile it: Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby mkdir /tmp/ruby && cd /tmp/ruby
curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p481.tar.gz | tar xz
cd ruby-2.0.0-p353 cd ruby-2.0.0-p481
./configure --disable-install-rdoc ./configure --disable-install-rdoc
make make
sudo make install sudo make install
...@@ -121,6 +121,7 @@ Create a `git` user for Gitlab: ...@@ -121,6 +121,7 @@ Create a `git` user for Gitlab:
# 4. Database # 4. Database
We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md). We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md).
NOTE: because we need to make use of extensions you need at least pgsql 9.1.
# Install the database packages # Install the database packages
sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev
...@@ -129,7 +130,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -129,7 +130,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
sudo -u postgres psql -d template1 sudo -u postgres psql -d template1
# Create a user for GitLab. # Create a user for GitLab.
template1=# CREATE USER git; template1=# CREATE USER git CREATEDB;
# Create the GitLab production database & grant all privileges on database # Create the GitLab production database & grant all privileges on database
template1=# CREATE DATABASE gitlabhq_production OWNER git; template1=# CREATE DATABASE gitlabhq_production OWNER git;
...@@ -149,13 +150,13 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -149,13 +150,13 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
## Clone the Source ## Clone the Source
# Clone GitLab repository # Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-8-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-9-stable gitlab
# Go to gitlab dir # Go to gitlab dir
cd /home/git/gitlab cd /home/git/gitlab
**Note:** **Note:**
You can change `6-8-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! You can change `6-9-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
## Configure it ## Configure it
...@@ -200,7 +201,7 @@ You can change `6-8-stable` to `master` if you want the *bleeding edge* version, ...@@ -200,7 +201,7 @@ You can change `6-8-stable` to `master` if you want the *bleeding edge* version,
# Configure Git global settings for git user, useful when editing via web # Configure Git global settings for git user, useful when editing via web
# Edit user.email according to what is set in gitlab.yml # Edit user.email according to what is set in gitlab.yml
sudo -u git -H git config --global user.name "GitLab" sudo -u git -H git config --global user.name "GitLab"
sudo -u git -H git config --global user.email "gitlab@localhost" sudo -u git -H git config --global user.email "example@example.com"
sudo -u git -H git config --global core.autocrlf input sudo -u git -H git config --global core.autocrlf input
**Important Note:** **Important Note:**
...@@ -243,15 +244,6 @@ that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2. ...@@ -243,15 +244,6 @@ that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
# Or if you use MySQL (note, the option says "without ... postgres") # Or if you use MySQL (note, the option says "without ... postgres")
sudo -u git -H bundle install --deployment --without development test postgres aws sudo -u git -H bundle install --deployment --without development test postgres aws
## Initialize Database and Activate Advanced Features
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
# Type 'yes' to create the database tables.
# When done you see 'Administrator account created:'
## Install GitLab shell ## Install GitLab shell
GitLab Shell is an ssh access and repository management software developed specially for GitLab. GitLab Shell is an ssh access and repository management software developed specially for GitLab.
...@@ -260,11 +252,20 @@ GitLab Shell is an ssh access and repository management software developed speci ...@@ -260,11 +252,20 @@ GitLab Shell is an ssh access and repository management software developed speci
cd /home/git/gitlab cd /home/git/gitlab
# Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
sudo -u git -H bundle exec rake gitlab:shell:install[v1.9.3] REDIS_URL=redis://localhost:6379 sudo -u git -H bundle exec rake gitlab:shell:install[v1.9.4] REDIS_URL=redis://localhost:6379 RAILS_ENV=production
# By default, the gitlab-shell config is generated from your main gitlab config. You can review (and modify) it as follows: # By default, the gitlab-shell config is generated from your main gitlab config. You can review (and modify) it as follows:
sudo -u git -H editor /home/git/gitlab-shell/config.yml sudo -u git -H editor /home/git/gitlab-shell/config.yml
## Initialize Database and Activate Advanced Features
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
# Type 'yes' to create the database tables.
# When done you see 'Administrator account created:'
## Install Init Script ## Install Init Script
Download the init script (will be /etc/init.d/gitlab): Download the init script (will be /etc/init.d/gitlab):
...@@ -302,11 +303,6 @@ Check if GitLab and its environment are configured correctly: ...@@ -302,11 +303,6 @@ Check if GitLab and its environment are configured correctly:
sudo /etc/init.d/gitlab restart sudo /etc/init.d/gitlab restart
## Compile assets
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
# 6. Nginx # 6. Nginx
**Note:** **Note:**
...@@ -413,22 +409,22 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ...@@ -413,22 +409,22 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already
These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation.
* Stop GitLab * Stop GitLab
`sudo service gitlab stop` `sudo service gitlab stop`
* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example) as a reference) * Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example) as a reference)
* Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile) * Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile)
`gem "omniauth-your-auth-provider"` `gem "omniauth-your-auth-provider"`
* If you're using MySQL, install the new Omniauth provider gem by running the following command: * If you're using MySQL, install the new Omniauth provider gem by running the following command:
`sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment` `sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment`
* If you're using PostgreSQL, install the new Omniauth provider gem by running the following command: * If you're using PostgreSQL, install the new Omniauth provider gem by running the following command:
`sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment` `sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment`
> These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`. > These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`.
* Start GitLab * Start GitLab
`sudo service gitlab start` `sudo service gitlab start`
### Examples ### Examples
......
...@@ -53,7 +53,7 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) but GitLab ...@@ -53,7 +53,7 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) but GitLab
## Memory ## Memory
- 512MB is the abolute minimum, you need 256MB of swap, you can configure only one slow unicorn worker, only ssh access will work, we do not recommend this - 512MB is the absolute minimum, you need 256MB of swap, you can configure only one slow unicorn worker, only ssh access will work, we do not recommend this
- 1GB supports up to 100 users (with individual repositories under 250MB, otherwise git memory usage necessitates using swap space) - 1GB supports up to 100 users (with individual repositories under 250MB, otherwise git memory usage necessitates using swap space)
- **2GB** is the **recommended** memory size and supports up to 500 users - **2GB** is the **recommended** memory size and supports up to 500 users
- 4GB supports up to 2,000 users - 4GB supports up to 2,000 users
...@@ -74,11 +74,14 @@ Apart from a local hard drive you can also mount a volume that supports the netw ...@@ -74,11 +74,14 @@ Apart from a local hard drive you can also mount a volume that supports the netw
If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab. If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
## Database
If you want to run the database separately, the **recommended** database size is **1 MB per user**
# Supported webbrowsers # Supported webbrowsers
- Chrome (Latest stable version) - Chrome (Latest stable version)
- Firefox (Latest released version) - Firefox (Latest released version)
- Safari 7+ (Know problem: required fields in html5 do not work) - Safari 7+ (known problem: required fields in html5 do not work)
- Opera (Latest released version) - Opera (Latest released version)
- IE 10+ - IE 10+
...@@ -7,3 +7,5 @@ See the documentation below for details on how to configure these services. ...@@ -7,3 +7,5 @@ See the documentation below for details on how to configure these services.
+ [LDAP](ldap.md) Set up sign in via LDAP + [LDAP](ldap.md) Set up sign in via LDAP
+ [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth. + [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth.
+ [Slack](slack.md) Integrate with the Slack chat service + [Slack](slack.md) Integrate with the Slack chat service
Jenkins support is [available in GitLab EE](http://doc.gitlab.com/ee/integration/jenkins.html).
...@@ -2,8 +2,10 @@ GitLab has a great issue tracker but you can also use an external issue tracker ...@@ -2,8 +2,10 @@ GitLab has a great issue tracker but you can also use an external issue tracker
- the 'Issues' link on the GitLab project pages takes you to the appropriate JIRA issue index; - the 'Issues' link on the GitLab project pages takes you to the appropriate JIRA issue index;
- clicking 'New issue' on the project dashboard creates a new JIRA issue; - clicking 'New issue' on the project dashboard creates a new JIRA issue;
- To reference JIRA issue PROJECT-1234 in comments, use syntax #PROJECT-1234. Commit messages get turned into HTML links to the corresponding JIRA issue. - To reference JIRA issue PROJECT-1234 in comments, use syntax PROJECT-1234. Commit messages get turned into HTML links to the corresponding JIRA issue.
![jira screenshot](jira-integration-points.png) ![jira screenshot](jira-integration-points.png)
You can configure the integration in the gitlab.yml configuration file. You can configure the integration in the gitlab.yml configuration file.
\ No newline at end of file
Support to add your commits to the Jira ticket automatically is [available in GitLab EE](http://doc.gitlab.com/ee/integration/jira.html).
...@@ -4,7 +4,7 @@ Internal projects will only be available to authenticated users. ...@@ -4,7 +4,7 @@ Internal projects will only be available to authenticated users.
#### Public projects #### Public projects
Public projects can be cloned **without any** authentication. Public projects can be cloned **without any** authentication.
It will also be listen on the [public access directory](/public). It will also be listed on the [public access directory](/public).
**Any logged in user** will have [Guest](/help/permissions) permissions on the repository. **Any logged in user** will have [Guest](/help/permissions) permissions on the repository.
#### Internal projects #### Internal projects
......
...@@ -24,9 +24,9 @@ Version: 5.1.0.beta2 ...@@ -24,9 +24,9 @@ Version: 5.1.0.beta2
Revision: 4da8b37 Revision: 4da8b37
Directory: /home/git/gitlab Directory: /home/git/gitlab
DB Adapter: mysql2 DB Adapter: mysql2
URL: http://localhost URL: http://example.com
HTTP Clone URL: http://localhost/some-project.git HTTP Clone URL: http://example.com/some-project.git
SSH Clone URL: git@localhost:some-project.git SSH Clone URL: git@example.com:some-project.git
Using LDAP: no Using LDAP: no
Using Omniauth: no Using Omniauth: no
......
# Things to do when creating new monthly minor or major release # Monthly Release
NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update). NOTE: This is a guide for GitLab developers.
## Install guide up to date? # **15th - Code Freeze & Release Manager**
* References correct GitLab branch `x-x-stable` and correct GitLab shell tag? ### **1. Stop merging in code, except for important bugfixes**
## Make upgrade guide ### **2. Release Manager**
### From x.x to x.x A release manager is selected that coordinates the entire release of this version. The release manager has to make sure all the steps below are done and delegated where necessary. This person should also make sure this document is kept up to date and issues are created and updated.
#### 0. Any major changes? Database updates? Web server change? File structure changes? # **18th - Releasing RC1**
The RC1 release comes with the task to update the installation and upgrade docs. Be mindful that there might already be merge requests for this on GitLab or GitHub.
### **1. Create an issue for RC1 release**
### **2. Update the installation guide**
1. Check if it references the correct branch `x-x-stable` (doesn't exist yet, but that is okay)
2. Check the [GitLab Shell version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L782)
3. Check the [Git version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L794)
4. There might be other changes. Ask around.
### **3. Create an update guide**
It's best to copy paste the previous guide and make changes where necessary. The typical steps are listed below with any points you should specifically look at.
#### 0. Any major changes?
List any major changes here, so the user is aware of them before starting to upgrade. For instance:
- Database updates
- Web server changes
- File structure changes
#### 1. Make backup #### 1. Make backup
...@@ -17,9 +38,9 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab ...@@ -17,9 +38,9 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab
#### 3. Do users need to update dependencies like `git`? #### 3. Do users need to update dependencies like `git`?
- Check the [GitLab Shell version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L782) - Check if the [GitLab Shell version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L782) changed since the last release.
- Check the [Git version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L794) - Check if the [Git version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L794) changed since the last release.
#### 4. Get latest code #### 4. Get latest code
...@@ -29,7 +50,7 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab ...@@ -29,7 +50,7 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab
#### 7. Any config files updated since last release? #### 7. Any config files updated since last release?
Check if any of these changed since last release (~22nd of last month depending on when last release branch was created): Check if any of these changed since last release:
* https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab * https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab
* https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example * https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example
...@@ -40,13 +61,14 @@ Check if any of these changed since last release (~22nd of last month depending ...@@ -40,13 +61,14 @@ Check if any of these changed since last release (~22nd of last month depending
#### 8. Need to update init script? #### 8. Need to update init script?
Check if changed since last release (~22nd of last month depending on when last release branch was created): https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/init.d/gitlab Check if the init.d/gitlab script changed since last release: https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/init.d/gitlab
#### 9. Start application #### 9. Start application
#### 10. Check application status #### 10. Check application status
## Make sure the code quality indicatiors are good ### **4. Code quality indicatiors**
Make sure the code quality indicators are green / good.
* [![build status](http://ci.gitlab.org/projects/1/status.png?ref=master)](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) * [![build status](http://ci.gitlab.org/projects/1/status.png?ref=master)](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
...@@ -58,49 +80,88 @@ Check if changed since last release (~22nd of last month depending on when last ...@@ -58,49 +80,88 @@ Check if changed since last release (~22nd of last month depending on when last
* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq) * [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
## Release Schedule ### **5. Set VERSION**
After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. The days of the month are approximately as follows: Set VERSION tot x.x.0.rc1
* 1-7th: Official merge window (see contributing guide).
* 8-14th: Work on bugfixes, sponsored features and GitLab EE. ### **6. Tag**
* 15th: Code freeze
- Stop merging into master, except essential bugfixes Create an annotated tag that points to the version change commit.
- Select a Release Manager ```
* 18th: Release Candidate 1 git tag -a vx.x.0.rc1 -m 'Version x.x.0.rc1'
- Set VERSION to x.x.0.rc1 ```
- Create annotated tag x.x.0.rc1
- Push the changes to GitLab.com, dev.gitlab.com, GitHub ### **7. Tweet**
- Tweet about the release
- Create a new branch on cloud for rc1 Tweet about the RC release:
- Deploy the new branch on Cloud after tests pass
* 20st: Optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) > GitLab x.x.x.rc1 is out. This is a release candidate intended for testing only. Please let us know if you find regressions.
* 22nd: Release
- Create x-x-stable branch and push to the repositories ### **8. Update Cloud**
- QA
- Fix anything coming out of the QA Merge the RC1 code into Cloud. Once the build is green, deploy in the morning.
- Set VERSION to x.x.0
- Create annotated tag x.x.0 It is important to do this as soon as possible, so we can catch any errors before we release the full version.
- Push VERSION + Tag to master, merge into x-x-stable
- Publish blog for new release
- Tweet to blog (see below)
* 22th: release GitLab EE
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
* 25th: release GitLab CI
# Write a blog post
* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
* Select and thank the the Most Valuable Person (MVP) of this release.
* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
# Tweet # **22nd - Release CE and EE**
Send out a tweet to share the good news with the world. For a major/minor release, list the features in short and link to the blog post. For GitLab EE, append -ee to the branches and tags.
For a RC, make sure to explain what a RC is. `x-x-stable-ee`
`v.x.x.0-ee`
### **1. Create x-x-stable branch and push to the repositories**
```
git checkout master
git pull
git checkout -b x-x-stable
git push <remote> x-x-stable
```
### **2. Build the Omnibus packages**
[Follow this guide](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md)
### **3. QA**
Use the omnibus packages to test using [this guide](https://dev.gitlab.org/gitlab/gitlab-ee/blob/master/doc/release/manual_testing.md)
### **4. Fix anything coming out of the QA**
### **5. Set VERSION to x.x.0**
### **6. Create annotated tag vx.x.0**
```
git tag -a vx.x.0 -m 'Version x.x.0'
```
### **7. Push VERSION + Tag to master, merge into x-x-stable**
```
git push origin master
```
Next, merge the VERSION into the x-x-stable branch.
### **8. Push to remotes**
For GitLab CE, push to dev, GitLab.com and GitHub.
For GitLab EE, push to the subscribers repo.
NOTE: You might not have the rights to push to master on dev. Ask Dmitriy.
### **9. Publish blog for new release**
* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
* Select and thank the the Most Valuable Person (MVP) of this release.
* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
A patch release tweet should specify the fixes it brings and link to the corresponding blog post. ### **10. Tweet to blog**
Send out a tweet to share the good news with the world. List the features in short and link to the blog post.
# **23rd - Optional Patch Release**
# **25th - Release GitLab CI**
...@@ -16,6 +16,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ...@@ -16,6 +16,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"path": "stormcloud", "path": "stormcloud",
"path_with_namespace": "jsmith/stormcloud", "path_with_namespace": "jsmith/stormcloud",
"project_id": 74, "project_id": 74,
"project_visibility": "private",
} }
``` ```
...@@ -31,6 +32,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ...@@ -31,6 +32,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"path": "underscore", "path": "underscore",
"path_with_namespace": "jsmith/underscore", "path_with_namespace": "jsmith/underscore",
"project_id": 73, "project_id": 73,
"project_visibility": "internal",
} }
``` ```
...@@ -38,14 +40,15 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ...@@ -38,14 +40,15 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
```json ```json
{ {
"created_at": "2012-07-21T07:30:56Z", "created_at": "2012-07-21T07:30:56Z",
"event_name": "user_add_to_team", "event_name": "user_add_to_team",
"project_access": "Master", "project_access": "Master",
"project_id": 74, "project_id": 74,
"project_name": "StoreCloud", "project_name": "StoreCloud",
"project_path": "storecloud", "project_path": "storecloud",
"user_email": "johnsmith@gmail.com", "user_email": "johnsmith@gmail.com",
"user_name": "John Smith", "user_name": "John Smith",
"project_visibility": "private",
} }
``` ```
...@@ -53,14 +56,15 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ...@@ -53,14 +56,15 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
```json ```json
{ {
"created_at": "2012-07-21T07:30:56Z", "created_at": "2012-07-21T07:30:56Z",
"event_name": "user_remove_from_team", "event_name": "user_remove_from_team",
"project_access": "Master", "project_access": "Master",
"project_id": 74, "project_id": 74,
"project_name": "StoreCloud", "project_name": "StoreCloud",
"project_path": "storecloud", "project_path": "storecloud",
"user_email": "johnsmith@gmail.com", "user_email": "johnsmith@gmail.com",
"user_name": "John Smith", "user_name": "John Smith",
"project_visibility": "private",
} }
``` ```
......
...@@ -64,6 +64,10 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ...@@ -64,6 +64,10 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
# Update the logrotate configuration (keep logs for 90 days instead of 52 weeks) # Update the logrotate configuration (keep logs for 90 days instead of 52 weeks)
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
# Compress existing .log.1 files because we turned off delaycompress in logrotate
sudo -u git -H gzip /home/git/gitlab/log/*.log.1
sudo -u git -H gzip /home/git/gitlab-shell/gitlab-shell.log.1
# Close access to gitlab-satellites for others # Close access to gitlab-satellites for others
sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
``` ```
......
...@@ -64,9 +64,6 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS ...@@ -64,9 +64,6 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab sudo chmod +x /etc/init.d/gitlab
# Update the logrotate configuration (keep logs for 90 days instead of 52 weeks)
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
# Close access to gitlab-satellites for others # Close access to gitlab-satellites for others
sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
``` ```
......
# From 6.8 to 6.9
### 0. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 1. Stop server
```bash
sudo service gitlab stop
```
### 2. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all
```
For Gitlab Community Edition:
```bash
sudo -u git -H git checkout 6-9-stable
```
OR
For GitLab Enterprise Edition:
```bash
sudo -u git -H git checkout 6-9-stable-ee
```
### 3. Update gitlab-shell (and its config)
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v1.9.4
```
### 4. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without ... postgres')
sudo -u git -H bundle install --without development test postgres --deployment
# PostgreSQL installations (note: the line below states '--without ... mysql')
sudo -u git -H bundle install --without development test mysql --deployment
```
### 5. Update config files
#### New configuration options for gitlab.yml
There are new configuration options available for gitlab.yml. View them with the command below and apply them to your current gitlab.yml if desired.
```
git diff 6-8-stable:config/gitlab.yml.example 6-9-stable:config/gitlab.yml.example
```
### 6. Start application
sudo service gitlab start
sudo service nginx restart
### 7. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade is complete!
## Things went south? Revert to previous version (6.8)
### 1. Revert the code to the previous version
Follow the [`upgrade guide from 6.7 to 6.8`](6.7-to-6.8.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
# Use the shell commands below to convert a MySQL GitLab database to a PostgreSQL one. # Migrating GitLab from MySQL to Postgres
If you are replacing MySQL with Postgres while keeping GitLab on the same
server all you need to do is to export from MySQL, import into Postgres and
rebuild the indexes as described below. If you are also moving GitLab to
another server, or if you are switching to omnibus-gitlab, you may want to use
a GitLab backup file. The second part of this documents explains the procedure
to do this.
## Export from MySQL and import into Postgres
Use this if you are keeping GitLab on the same server.
``` ```
git clone https://github.com/lanyrd/mysql-postgresql-converter.git sudo service gitlab stop
# Update /home/git/gitlab/config/database.yml
git clone https://github.com/gitlabhq/mysql-postgresql-converter.git
cd mysql-postgresql-converter cd mysql-postgresql-converter
mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production
python db_converter.py databasename.mysql databasename.psql python db_converter.py databasename.mysql databasename.psql
psql -f databasename.psql -d gitlabhq_production psql -f databasename.psql -d gitlabhq_production
# Rebuild indexes (see below)
sudo service gitlab start
```
## Rebuild indexes
The lanyrd database converter script does not preserve all indexes, so we have
to recreate them ourselves after migrating from MySQL. It is not necessary to
shut down GitLab for this process.
```
# Clone the database converter on your Postgres-backed GitLab server
cd /tmp
git clone https://github.com/gitlabhq/mysql-postgresql-converter.git
# Stash changes to db/schema.rb to make sure we can find the right index statements
cd /home/git/gitlab
sudo -u git -H git stash
# Generate the `CREATE INDEX CONCURRENTLY` statements based on schema.rb
cd /tmp/mysql-to-postgresql-converter
ruby index_create_statements.rb /home/git/gitlab/db/schema.rb > index_create_statements.psql
# Execute the SQL statements against the GitLab database
sudo -u git psql -f index_create_statements.psql -d gitlabhq_production
```
## Converting a GitLab backup file from MySQL to Postgres
GitLab backup files (<timestamp>_gitlab_backup.tar) contain a SQL dump. Using
the lanyrd database converter we can replace a MySQL database dump inside the
tar file with a Postgres database dump. This can be useful if you are moving to
another server.
```
# Stop GitLab
sudo service gitlab stop
# Create the backup
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
# Note the filename of the backup that was created. We will call it
# TIMESTAMP_gitlab_backup.tar below.
# Move the backup file we will convert to its own directory
sudo -u git -H mkdir -p tmp/backups/postgresql
sudo -u git -H mv tmp/backups/TIMESTAMP_gitlab_backup.tar tmp/backups/postgresql/
# Create a separate database dump with PostgreSQL compatibility
cd tmp/backups/postgresql
sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production
# Clone the database converter
sudo -u git -H git clone https://github.com/lanyrd/mysql-postgresql-converter.git
# Convert gitlabhq_production.mysql
sudo -u git -H mkdir db
sudo -u git -H python mysql-postgresql-converter/db_converter.py gitlabhq_production.mysql db/database.sql
# Replace the MySQL dump in TIMESTAMP_gitlab_backup.tar.
# Warning: if you forget to replace TIMESTAMP below, tar will create a new file
# 'TIMESTAMP_gitlab_backup.tar' without giving an error.
sudo -u git -H tar rf TIMESTAMP_gitlab_backup.tar db/database.sql
# Done! TIMESTAMP_gitlab_backup.tar can now be restored into a Postgres GitLab
# installation. Remember to recreate the indexes after the import.
``` ```
# Updating Ruby from source # Updating Ruby from source
This guide explains how to update Ruby in case you installed it from source according to the instructions in https://gitlab.com/gitlab-org/gitlab-ce/blob/masterdoc/install/installation.md#2-ruby . This guide explains how to update Ruby in case you installed it from source according to the [instructions](../install/installation.md#2-ruby).
### 1. Look for Ruby versions ### 1. Look for Ruby versions
This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running: This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running:
...@@ -36,7 +36,7 @@ sudo gem install bundler ...@@ -36,7 +36,7 @@ sudo gem install bundler
``` ```
### 5. Reinstall GitLab gem bundle ### 5. Reinstall GitLab gem bundle
Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](https://gitlab.com/gitlab-org/gitlab-ce/blob/masterdoc/install/installation.md#install-gems). Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](../install/installation.md#install-gems).
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
......
...@@ -25,16 +25,16 @@ Triggered when you push to the repository except when pushing tags. ...@@ -25,16 +25,16 @@ Triggered when you push to the repository except when pushing tags.
"project_id": 15, "project_id": 15,
"repository": { "repository": {
"name": "Diaspora", "name": "Diaspora",
"url": "git@localhost:diaspora.git", "url": "git@example.com:diaspora.git",
"description": "", "description": "",
"homepage": "http://localhost/diaspora" "homepage": "http://example.com/diaspora"
}, },
"commits": [ "commits": [
{ {
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.", "message": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00", "timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "url": "http://example.com/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": { "author": {
"name": "Jordi Mallach", "name": "Jordi Mallach",
"email": "jordi@softcatala.org" "email": "jordi@softcatala.org"
...@@ -44,7 +44,7 @@ Triggered when you push to the repository except when pushing tags. ...@@ -44,7 +44,7 @@ Triggered when you push to the repository except when pushing tags.
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme", "message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00", "timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "url": "http://example.com/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": { "author": {
"name": "GitLab dev user", "name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)" "email": "gitlabdev@dv6700.(none)"
...@@ -112,3 +112,34 @@ Triggered when a new merge request is created or an existing merge request was u ...@@ -112,3 +112,34 @@ Triggered when a new merge request is created or an existing merge request was u
} }
} }
``` ```
#### Example webhook receiver
If you want to see GitLab's webhooks in action for testing purposes you can use
a simple echo script running in a console session.
Save the following file as `print_http_body.rb`.
```ruby
require 'webrick'
server = WEBrick::HTTPServer.new(Port: ARGV.first)
server.mount_proc '/' do |req, res|
puts req.body
end
trap 'INT' do server.shutdown end
server.start
```
Pick an unused port (e.g. 8000) and start the script: `ruby print_http_body.rb
8000`. Then add your server as a webhook receiver in GitLab as
`http://my.host:8000/`.
When you press 'Test Hook' in GitLab, you should see something like this in the console.
```
{"before":"077a85dd266e6f3573ef7e9ef8ce3343ad659c4e","after":"95cd4a99e93bc4bbabacfa2cd10e6725b1403c60",<SNIP>}
example.com - - [14/May/2014:07:45:26 EDT] "POST / HTTP/1.1" 200 0
- -> /
```
...@@ -113,3 +113,10 @@ Feature: Groups ...@@ -113,3 +113,10 @@ Feature: Groups
Then I should see user "John Doe" in team list Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list Then I should see user "Mary Jane" in team list
Then I should not see the "Remove User From Group" button for "Mary Jane" Then I should not see the "Remove User From Group" button for "Mary Jane"
Scenario: Search member by name
Given "Mary Jane" is guest of group "Guest"
And I visit group "Guest" members page
When I search for 'Mary' member
Then I should see user "Mary Jane" in team list
Then I should not see user "John Doe" in team list
...@@ -30,11 +30,10 @@ Feature: Project Forked Merge Requests ...@@ -30,11 +30,10 @@ Feature: Project Forked Merge Requests
Given I visit project "Forked Shop" merge requests page Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request" And I click link "New Merge Request"
And I fill out an invalid "Merge Request On Forked Project" merge request And I fill out an invalid "Merge Request On Forked Project" merge request
And I submit the merge request
Then I should see validation errors Then I should see validation errors
@javascript @javascript
Scenario: Merge request should target fork repository by default Scenario: Merge request should target fork repository by default
Given I visit project "Forked Shop" merge requests page Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request" And I click link "New Merge Request"
Then the target repository should be the original repository Then the target repository should be the original repository
\ No newline at end of file
...@@ -45,3 +45,20 @@ Feature: Project Wiki ...@@ -45,3 +45,20 @@ Feature: Project Wiki
And I browse to that Wiki page And I browse to that Wiki page
And I click on the "Pages" button And I click on the "Pages" button
Then I should see the existing page in the pages list Then I should see the existing page in the pages list
Scenario: File exists in wiki repo
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
And I click on existing image link
Then I should see the image from wiki repo
Scenario: Image in wiki repo shown on the page
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
Then Image should be shown on the page
Scenario: File does not exist in wiki repo
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
And I click on image link
Then I should see the new wiki page form
...@@ -25,7 +25,6 @@ class Dashboard < Spinach::FeatureSteps ...@@ -25,7 +25,6 @@ class Dashboard < Spinach::FeatureSteps
find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should == "new_design" find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master" find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New design"
end end
Given 'user with name "John Doe" joined project "Shop"' do Given 'user with name "John Doe" joined project "Shop"' do
......
...@@ -157,6 +157,13 @@ class Groups < Spinach::FeatureSteps ...@@ -157,6 +157,13 @@ class Groups < Spinach::FeatureSteps
# poltergeist always confirms popups. # poltergeist always confirms popups.
end end
step 'I search for \'Mary\' member' do
within '.member-search-form' do
fill_in 'search', with: 'Mary'
click_button 'Search'
end
end
protected protected
def assigned_to_me key def assigned_to_me key
......
...@@ -53,6 +53,7 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -53,6 +53,7 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps
find(:select, "merge_request_source_branch", {}).value.should == 'master' find(:select, "merge_request_source_branch", {}).value.should == 'master'
find(:select, "merge_request_target_branch", {}).value.should == 'stable' find(:select, "merge_request_target_branch", {}).value.should == 'stable'
click_button "Compare branches"
fill_in "merge_request_title", with: "Merge Request On Forked Project" fill_in "merge_request_title", with: "Merge Request On Forked Project"
end end
...@@ -148,29 +149,19 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -148,29 +149,19 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps
current_path.should == edit_project_merge_request_path(@project, @merge_request) current_path.should == edit_project_merge_request_path(@project, @merge_request)
page.should have_content "Edit merge request ##{@merge_request.id}" page.should have_content "Edit merge request ##{@merge_request.id}"
find("#merge_request_title").value.should == "Merge Request On Forked Project" find("#merge_request_title").value.should == "Merge Request On Forked Project"
find("#merge_request_source_project_id").value.should == @forked_project.id.to_s
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should have_content "master"
verify_commit_link(".mr_source_commit",@forked_project)
find("#merge_request_target_branch").value.should have_content "stable"
verify_commit_link(".mr_target_commit",@project)
end end
step 'I fill out an invalid "Merge Request On Forked Project" merge request' do step 'I fill out an invalid "Merge Request On Forked Project" merge request' do
#If this isn't filled in the rest of the validations won't be triggered
fill_in "merge_request_title", with: "Merge Request On Forked Project"
select "Select branch", from: "merge_request_target_branch" select "Select branch", from: "merge_request_target_branch"
find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s
find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s
find(:select, "merge_request_source_branch", {}).value.should == "" find(:select, "merge_request_source_branch", {}).value.should == ""
find(:select, "merge_request_target_branch", {}).value.should == "" find(:select, "merge_request_target_branch", {}).value.should == ""
click_button "Compare branches"
end end
step 'I should see validation errors' do step 'I should see validation errors' do
page.should have_content "Source branch can't be blank" page.should have_content "You must select source and target branch"
page.should have_content "Target branch can't be blank"
end end
step 'the target repository should be the original repository' do step 'the target repository should be the original repository' do
......
...@@ -61,9 +61,10 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -61,9 +61,10 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I submit new merge request "Wiki Feature"' do step 'I submit new merge request "Wiki Feature"' do
fill_in "merge_request_title", with: "Wiki Feature"
select "master", from: "merge_request_source_branch" select "master", from: "merge_request_source_branch"
select "notes_refactoring", from: "merge_request_target_branch" select "notes_refactoring", from: "merge_request_target_branch"
click_button "Compare branches"
fill_in "merge_request_title", with: "Wiki Feature"
click_button "Submit merge request" click_button "Submit merge request"
end end
......
...@@ -86,6 +86,47 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps ...@@ -86,6 +86,47 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
page.should have_content @page.title page.should have_content @page.title
end end
Given 'I have an existing Wiki page with images linked on page' do
wiki.create_page("pictures", "Look at this [image](image.jpg)\n\n ![image](image.jpg)", :markdown, "first commit")
@wiki_page = wiki.find_page("pictures")
end
And 'I browse to wiki page with images' do
visit project_wiki_path(project, @wiki_page)
end
And 'I click on existing image link' do
file = Gollum::File.new(wiki.wiki)
Gollum::Wiki.any_instance.stub(:file).with("image.jpg", "master", true).and_return(file)
Gollum::File.any_instance.stub(:mime_type).and_return("image/jpeg")
page.should have_link('image', href: "image.jpg")
click_on "image"
end
Then 'I should see the image from wiki repo' do
url = URI.parse(current_url)
url.path.should match("wikis/image.jpg")
page.should_not have_xpath('/html') # Page should render the image which means there is no html involved
Gollum::Wiki.any_instance.unstub(:file)
Gollum::File.any_instance.unstub(:mime_type)
end
Then 'Image should be shown on the page' do
page.should have_xpath("//img[@src=\"image.jpg\"]")
end
And 'I click on image link' do
page.should have_link('image', href: "image.jpg")
click_on "image"
end
Then 'I should see the new wiki page form' do
url = URI.parse(current_url)
url.path.should match("wikis/image.jpg")
page.should have_content('New Wiki Page')
page.should have_content('Editing - image.jpg')
end
def wiki def wiki
@project_wiki = ProjectWiki.new(project, current_user) @project_wiki = ProjectWiki.new(project, current_user)
end end
......
...@@ -24,7 +24,7 @@ module API ...@@ -24,7 +24,7 @@ module API
# branch (required) - The name of the branch # branch (required) - The name of the branch
# Example Request: # Example Request:
# GET /projects/:id/repository/branches/:branch # GET /projects/:id/repository/branches/:branch
get ":id/repository/branches/:branch" do get ':id/repository/branches/:branch', requirements: { branch: /.*/ } do
@branch = user_project.repo.heads.find { |item| item.name == params[:branch] } @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
not_found!("Branch does not exist") if @branch.nil? not_found!("Branch does not exist") if @branch.nil?
present @branch, with: Entities::RepoObject, project: user_project present @branch, with: Entities::RepoObject, project: user_project
...@@ -37,7 +37,9 @@ module API ...@@ -37,7 +37,9 @@ module API
# branch (required) - The name of the branch # branch (required) - The name of the branch
# Example Request: # Example Request:
# PUT /projects/:id/repository/branches/:branch/protect # PUT /projects/:id/repository/branches/:branch/protect
put ":id/repository/branches/:branch/protect" do put ':id/repository/branches/:branch/protect',
requirements: { branch: /.*/ } do
authorize_admin_project authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch]) @branch = user_project.repository.find_branch(params[:branch])
...@@ -55,7 +57,9 @@ module API ...@@ -55,7 +57,9 @@ module API
# branch (required) - The name of the branch # branch (required) - The name of the branch
# Example Request: # Example Request:
# PUT /projects/:id/repository/branches/:branch/unprotect # PUT /projects/:id/repository/branches/:branch/unprotect
put ":id/repository/branches/:branch/unprotect" do put ':id/repository/branches/:branch/unprotect',
requirements: { branch: /.*/ } do
authorize_admin_project authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch]) @branch = user_project.repository.find_branch(params[:branch])
......
...@@ -43,6 +43,7 @@ module API ...@@ -43,6 +43,7 @@ module API
class Project < Grape::Entity class Project < Grape::Entity
expose :id, :description, :default_branch expose :id, :description, :default_branch
expose :public?, as: :public expose :public?, as: :public
expose :archived?, as: :archived
expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace expose :name, :name_with_namespace
...@@ -135,6 +136,7 @@ module API ...@@ -135,6 +136,7 @@ module API
expose :target_branch, :source_branch, :upvotes, :downvotes expose :target_branch, :source_branch, :upvotes, :downvotes
expose :author, :assignee, using: Entities::UserBasic expose :author, :assignee, using: Entities::UserBasic
expose :source_project_id, :target_project_id expose :source_project_id, :target_project_id
expose :label_list, as: :labels
end end
class SSHKey < Grape::Entity class SSHKey < Grape::Entity
......
...@@ -8,6 +8,11 @@ module API ...@@ -8,6 +8,11 @@ module API
def current_user def current_user
private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
@current_user ||= User.find_by(authentication_token: private_token) @current_user ||= User.find_by(authentication_token: private_token)
unless @current_user && Gitlab::UserAccess.allowed?(@current_user)
return nil
end
identifier = sudo_identifier() identifier = sudo_identifier()
# If the sudo is the current user do nothing # If the sudo is the current user do nothing
......
...@@ -34,7 +34,7 @@ module API ...@@ -34,7 +34,7 @@ module API
when "closed" then user_project.merge_requests.closed when "closed" then user_project.merge_requests.closed
when "merged" then user_project.merge_requests.merged when "merged" then user_project.merge_requests.merged
else user_project.merge_requests else user_project.merge_requests
end end
present paginate(mrs), with: Entities::MergeRequest present paginate(mrs), with: Entities::MergeRequest
end end
...@@ -67,6 +67,7 @@ module API ...@@ -67,6 +67,7 @@ module API
# assignee_id - Assignee user ID # assignee_id - Assignee user ID
# title (required) - Title of MR # title (required) - Title of MR
# description - Description of MR # description - Description of MR
# labels (optional) - Labels for MR as a comma-separated list
# #
# Example: # Example:
# POST /projects/:id/merge_requests # POST /projects/:id/merge_requests
...@@ -75,6 +76,7 @@ module API ...@@ -75,6 +76,7 @@ module API
authorize! :write_merge_request, user_project authorize! :write_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title] required_attributes! [:source_branch, :target_branch, :title]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description] attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
attrs[:label_list] = params[:labels] if params[:labels].present?
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute
if merge_request.valid? if merge_request.valid?
...@@ -95,11 +97,13 @@ module API ...@@ -95,11 +97,13 @@ module API
# title - Title of MR # title - Title of MR
# state_event - Status of MR. (close|reopen|merge) # state_event - Status of MR. (close|reopen|merge)
# description - Description of MR # description - Description of MR
# labels (optional) - Labels for a MR as a comma-separated list
# Example: # Example:
# PUT /projects/:id/merge_request/:merge_request_id # PUT /projects/:id/merge_request/:merge_request_id
# #
put ":id/merge_request/:merge_request_id" do put ":id/merge_request/:merge_request_id" do
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description] attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
attrs[:label_list] = params[:labels] if params[:labels].present?
merge_request = user_project.merge_requests.find(params[:merge_request_id]) merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :modify_merge_request, merge_request authorize! :modify_merge_request, merge_request
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request) merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
...@@ -111,6 +115,49 @@ module API ...@@ -111,6 +115,49 @@ module API
end end
end end
# Merge MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# merge_commit_message (optional) - Custom merge commit message
# Example:
# PUT /projects/:id/merge_request/:merge_request_id/merge
#
put ":id/merge_request/:merge_request_id/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
action = if user_project.protected_branch?(merge_request.target_branch)
:push_code_to_protected_branches
else
:push_code
end
if can?(current_user, action, user_project)
if merge_request.unchecked?
merge_request.check_if_can_be_merged
end
if merge_request.open?
if merge_request.can_be_merged?
merge_request.automerge!(current_user, params[:merge_commit_message] || merge_request.merge_commit_message)
present merge_request, with: Entities::MergeRequest
else
render_api_error!('Branch cannot be merged', 405)
end
else
# Merge request can not be merged
# because it is already closed/merged
not_allowed!
end
else
# Merge request can not be merged
# because user dont have permissions to push into target branch
unauthorized!
end
end
# Get a merge request's comments # Get a merge request's comments
# #
# Parameters: # Parameters:
......
...@@ -101,7 +101,7 @@ module Backup ...@@ -101,7 +101,7 @@ module Backup
def tar_version def tar_version
tar_version, _ = Gitlab::Popen.popen(%W(tar --version)) tar_version, _ = Gitlab::Popen.popen(%W(tar --version))
tar_version.split("\n").first tar_version.force_encoding('locale').split("\n").first
end end
end end
end end
...@@ -10,15 +10,12 @@ module Backup ...@@ -10,15 +10,12 @@ module Backup
Project.find_each(batch_size: 1000) do |project| Project.find_each(batch_size: 1000) do |project|
print " * #{project.path_with_namespace} ... " print " * #{project.path_with_namespace} ... "
if project.empty_repo?
puts "[SKIPPED]".cyan
next
end
# Create namespace dir if missing # Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
if system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent) if project.empty_repo?
puts "[SKIPPED]".cyan
elsif system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent)
puts "[DONE]".green puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
......
...@@ -61,18 +61,7 @@ module Gitlab ...@@ -61,18 +61,7 @@ module Gitlab
private private
def user_allowed?(user) def user_allowed?(user)
return false if user.blocked? Gitlab::UserAccess.allowed?(user)
if Gitlab.config.ldap.enabled
if user.ldap_user?
# Check if LDAP user exists and match LDAP user_filter
unless Gitlab::LDAP::Access.new.allowed?(user)
return false
end
end
end
true
end end
end end
end end
...@@ -14,7 +14,11 @@ module Gitlab ...@@ -14,7 +14,11 @@ module Gitlab
end end
def allowed?(user) def allowed?(user)
!!Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter) if Gitlab::LDAP::Person.find_by_dn(user.extern_uid, adapter)
!Gitlab::LDAP::Person.active_directory_disabled?(user.extern_uid, adapter)
else
false
end
rescue rescue
false false
end end
......
...@@ -64,7 +64,7 @@ module Gitlab ...@@ -64,7 +64,7 @@ module Gitlab
end end
end end
entries = ldap.search(options).select do |entry| entries = ldap_search(options).select do |entry|
entry.respond_to? config.uid entry.respond_to? config.uid
end end
...@@ -77,6 +77,26 @@ module Gitlab ...@@ -77,6 +77,26 @@ module Gitlab
users(*args).first users(*args).first
end end
def dn_matches_filter?(dn, filter)
ldap_search(base: dn, filter: filter, scope: Net::LDAP::SearchScope_BaseObject, attributes: %w{dn}).any?
end
def ldap_search(*args)
results = ldap.search(*args)
if results.nil?
response = ldap.get_operation_result
unless response.code.zero?
Rails.logger.warn("LDAP search error: #{response.message}")
end
[]
else
results
end
end
private private
def config def config
......
module Gitlab module Gitlab
module LDAP module LDAP
class Person class Person
# Active Directory-specific LDAP filter that checks if bit 2 of the
# userAccountControl attribute is set.
# Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2")
def self.find_by_uid(uid, adapter=nil) def self.find_by_uid(uid, adapter=nil)
adapter ||= Gitlab::LDAP::Adapter.new adapter ||= Gitlab::LDAP::Adapter.new
adapter.user(config.uid, uid) adapter.user(config.uid, uid)
...@@ -11,6 +16,11 @@ module Gitlab ...@@ -11,6 +16,11 @@ module Gitlab
adapter.user('dn', dn) adapter.user('dn', dn)
end end
def self.active_directory_disabled?(dn, adapter=nil)
adapter ||= Gitlab::LDAP::Adapter.new
adapter.dn_matches_filter?(dn, AD_USER_DISABLED)
end
def initialize(entry) def initialize(entry)
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" } Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry @entry = entry
......
...@@ -98,6 +98,7 @@ module Gitlab ...@@ -98,6 +98,7 @@ module Gitlab
(?<prefix>\W)? # Prefix (?<prefix>\W)? # Prefix
( # Reference ( # Reference
@(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name @(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User name
|(?<issue>([A-Z\-]+-)\d+) # JIRA Issue ID
|\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID |\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
|!(?<merge_request>\d+) # MR ID |!(?<merge_request>\d+) # MR ID
|\$(?<snippet>\d+) # Snippet ID |\$(?<snippet>\d+) # Snippet ID
...@@ -172,11 +173,15 @@ module Gitlab ...@@ -172,11 +173,15 @@ module Gitlab
end end
def reference_issue(identifier) def reference_issue(identifier)
if @project.issue_exists? identifier if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = url_for_issue(identifier) if @project.issue_exists? identifier
title = title_for_issue(identifier) url = url_for_issue(identifier)
title = title_for_issue(identifier)
link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}")) link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
end
else
reference_jira_issue(identifier) if @project.issues_tracker == "jira"
end end
end end
...@@ -197,5 +202,12 @@ module Gitlab ...@@ -197,5 +202,12 @@ module Gitlab
link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: commit.link_title, class: "gfm gfm-commit #{html_options[:class]}")) link_to(identifier, project_commit_url(@project, commit), html_options.merge(title: commit.link_title, class: "gfm gfm-commit #{html_options[:class]}"))
end end
end end
def reference_jira_issue(identifier)
url = url_for_issue(identifier)
title = Gitlab.config.issues_tracker[@project.issues_tracker]["title"]
link_to("#{identifier}", url, html_options.merge(title: "Issue in #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
end
end end
end end
...@@ -34,9 +34,11 @@ module Gitlab ...@@ -34,9 +34,11 @@ module Gitlab
# In this case we generate temporary email and force user to fill it later # In this case we generate temporary email and force user to fill it later
if user.email.blank? if user.email.blank?
user.generate_tmp_oauth_email user.generate_tmp_oauth_email
else elsif provider != "ldap"
# Google oauth returns email but dont return nickname # Google oauth returns email but dont return nickname
# So we use part of email as username for new user # So we use part of email as username for new user
# For LDAP, username is already set to the user's
# uid/userid/sAMAccountName.
user.username = email.match(/^[^@]*/)[0] user.username = email.match(/^[^@]*/)[0]
end end
...@@ -65,7 +67,11 @@ module Gitlab ...@@ -65,7 +67,11 @@ module Gitlab
end end
def name def name
auth.info.name.to_s.force_encoding("utf-8") if auth.info.name.nil?
"#{auth.info.first_name} #{auth.info.last_name}".force_encoding('utf-8')
else
auth.info.name.to_s.force_encoding('utf-8')
end
end end
def username def username
......
module Gitlab
module Satellite
class CompareAction < Action
def initialize(user, target_project, target_branch, source_project, source_branch)
super user, target_project
@target_project, @target_branch = target_project, target_branch
@source_project, @source_branch = source_project, source_branch
end
# Only show what is new in the source branch compared to the target branch, not the other way around.
# The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
# From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
def diffs
in_locked_and_timed_satellite do |target_repo|
prepare_satellite!(target_repo)
update_satellite_source_and_target!(target_repo)
common_commit = target_repo.git.native(:merge_base, default_options, ["origin/#{@target_branch}", "source/#{@source_branch}"]).strip
#this method doesn't take default options
diffs = target_repo.diff(common_commit, "source/#{@source_branch}")
diffs = diffs.map { |diff| Gitlab::Git::Diff.new(diff) }
diffs
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
# Retrieve an array of commits between the source and the target
def commits
in_locked_and_timed_satellite do |target_repo|
prepare_satellite!(target_repo)
update_satellite_source_and_target!(target_repo)
commits = target_repo.commits_between("origin/#{@target_branch}", "source/#{@source_branch}")
commits = commits.map { |commit| Gitlab::Git::Commit.new(commit, nil) }
commits
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
private
# Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for diffs
def update_satellite_source_and_target!(target_repo)
target_repo.remote_add('source', @source_project.repository.path_to_repo)
target_repo.remote_fetch('source')
target_repo.git.checkout(default_options({b: true}), @target_branch, "origin/#{@target_branch}")
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
end
end
end
...@@ -84,6 +84,7 @@ module Gitlab ...@@ -84,6 +84,7 @@ module Gitlab
# Clear the working directory # Clear the working directory
def clear_working_dir! def clear_working_dir!
repo.git.reset(hard: true) repo.git.reset(hard: true)
repo.git.clean(f: true, d: true, x: true)
end end
# Deletes all branches except the parking branch # Deletes all branches except the parking branch
......
module Gitlab
module UserAccess
def self.allowed?(user)
return false if user.blocked?
if Gitlab.config.ldap.enabled
if user.ldap_user?
# Check if LDAP user exists and match LDAP user_filter
Gitlab::LDAP::Access.open do |adapter|
return false unless adapter.allowed?(user)
end
end
end
true
end
end
end
...@@ -59,6 +59,9 @@ server { ...@@ -59,6 +59,9 @@ server {
} }
# Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression # Enable gzip compression as per rails guide: http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
# WARNING: If you are using relative urls do remove the block below
# See config/application.rb under "Relative url support" for the list of
# other files that need to be changed for relative url support
location ~ ^/(assets)/ { location ~ ^/(assets)/ {
root /home/git/gitlab/public; root /home/git/gitlab/public;
gzip_static on; # to serve pre-gzipped version gzip_static on; # to serve pre-gzipped version
...@@ -67,4 +70,4 @@ server { ...@@ -67,4 +70,4 @@ server {
} }
error_page 502 /502.html; error_page 502 /502.html;
} }
\ No newline at end of file
...@@ -8,9 +8,9 @@ namespace :gitlab do ...@@ -8,9 +8,9 @@ namespace :gitlab do
] ]
cmds.each do |cmd| cmds.each do |cmd|
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) result = system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
raise "#{cmd} failed!" unless $?.exitstatus.zero? raise "#{cmd} failed!" unless result
end end
end end
end end
...@@ -181,6 +181,52 @@ describe GitlabMarkdownHelper do ...@@ -181,6 +181,52 @@ describe GitlabMarkdownHelper do
include_examples 'referenced object' include_examples 'referenced object'
end end
describe "referencing a Jira issue" do
let(:actual) { "Reference to JIRA-#{issue.iid}" }
let(:expected) { "http://jira.example/browse/JIRA-#{issue.iid}" }
let(:reference) { "JIRA-#{issue.iid}" }
before do
issue_tracker_config = { "jira" => { "title" => "JIRA tracker", "issues_url" => "http://jira.example/browse/:id" } }
Gitlab.config.stub(:issues_tracker).and_return(issue_tracker_config)
@project.stub(:issues_tracker).and_return("jira")
@project.stub(:issues_tracker_id).and_return("JIRA")
end
it "should link using a valid id" do
gfm(actual).should match(expected)
end
it "should link with adjacent text" do
# Wrap the reference in parenthesis
gfm(actual.gsub(reference, "(#{reference})")).should match(expected)
# Append some text to the end of the reference
gfm(actual.gsub(reference, "#{reference}, right?")).should match(expected)
end
it "should keep whitespace intact" do
actual = "Referenced #{reference} already."
expected = /Referenced <a.+>[^\s]+<\/a> already/
gfm(actual).should match(expected)
end
it "should not link with an invalid id" do
# Modify the reference string so it's still parsed, but is invalid
invalid_reference = actual.gsub(/(\d+)$/, "r45")
gfm(invalid_reference).should == invalid_reference
end
it "should include a title attribute" do
title = "Issue in JIRA tracker"
gfm(actual).should match(/title="#{title}"/)
end
it "should include standard gfm classes" do
gfm(actual).should match(/class="\s?gfm gfm-issue\s?"/)
end
end
describe "referencing a merge request" do describe "referencing a merge request" do
let(:object) { merge_request } let(:object) { merge_request }
let(:reference) { "!#{merge_request.iid}" } let(:reference) { "!#{merge_request.iid}" }
......
require 'spec_helper'
describe Gitlab::LDAP::Access do
let(:access) { Gitlab::LDAP::Access.new }
let(:user) { create(:user) }
describe :allowed? do
subject { access.allowed?(user) }
context 'when the user cannot be found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: nil) }
it { should be_false }
end
context 'when the user is found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: :ldap_user) }
context 'and the Active Directory disabled flag is set' do
before { Gitlab::LDAP::Person.stub(active_directory_disabled?: true) }
it { should be_false }
end
context 'and the Active Directory disabled flag is not set' do
before { Gitlab::LDAP::Person.stub(active_directory_disabled?: false) }
it { should be_true }
end
end
end
end
require 'spec_helper'
describe Gitlab::LDAP::Adapter do
let(:adapter) { Gitlab::LDAP::Adapter.new }
describe :dn_matches_filter? do
let(:ldap) { double(:ldap) }
subject { adapter.dn_matches_filter?(:dn, :filter) }
before { adapter.stub(ldap: ldap) }
context "when the search is successful" do
context "and the result is non-empty" do
before { ldap.stub(search: [:foo]) }
it { should be_true }
end
context "and the result is empty" do
before { ldap.stub(search: []) }
it { should be_false }
end
end
context "when the search encounters an error" do
before { ldap.stub(search: nil, get_operation_result: double(code: 1, message: 'some error')) }
it { should be_false }
end
end
end
...@@ -11,6 +11,12 @@ describe Gitlab::ReferenceExtractor do ...@@ -11,6 +11,12 @@ describe Gitlab::ReferenceExtractor do
subject.issues.should == ["1234"] subject.issues.should == ["1234"]
end end
it 'extracts JIRA issue references' do
Gitlab.config.gitlab.stub(:issues_tracker).and_return("jira")
subject.analyze "this one talks about issue JIRA-1234"
subject.issues.should == ["JIRA-1234"]
end
it 'extracts merge request references' do it 'extracts merge request references' do
subject.analyze "and here's !43, a merge request" subject.analyze "and here's !43, a merge request"
subject.merge_requests.should == ["43"] subject.merge_requests.should == ["43"]
......
...@@ -161,6 +161,10 @@ describe Notify do ...@@ -161,6 +161,10 @@ describe Notify do
it 'contains a link to the new issue' do it 'contains a link to the new issue' do
should have_body_text /#{project_issue_path project, issue}/ should have_body_text /#{project_issue_path project, issue}/
end end
it 'has the correct message-id set' do
should have_header 'Message-ID', "<issue_#{issue.id}@#{Gitlab.config.gitlab.host}>"
end
end end
describe 'that are new with a description' do describe 'that are new with a description' do
...@@ -197,6 +201,10 @@ describe Notify do ...@@ -197,6 +201,10 @@ describe Notify do
it 'contains a link to the issue' do it 'contains a link to the issue' do
should have_body_text /#{project_issue_path project, issue}/ should have_body_text /#{project_issue_path project, issue}/
end end
it 'has the correct reference set' do
should have_header 'References', "<issue_#{issue.id}@#{Gitlab.config.gitlab.host}>"
end
end end
describe 'status changed' do describe 'status changed' do
...@@ -224,6 +232,10 @@ describe Notify do ...@@ -224,6 +232,10 @@ describe Notify do
it 'contains a link to the issue' do it 'contains a link to the issue' do
should have_body_text /#{project_issue_path project, issue}/ should have_body_text /#{project_issue_path project, issue}/
end end
it 'has the correct reference set' do
should have_header 'References', "<issue_#{issue.id}@#{Gitlab.config.gitlab.host}>"
end
end end
end end
...@@ -239,7 +251,7 @@ describe Notify do ...@@ -239,7 +251,7 @@ describe Notify do
it_behaves_like 'an assignee email' it_behaves_like 'an assignee email'
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains a link to the new merge request' do it 'contains a link to the new merge request' do
...@@ -253,6 +265,10 @@ describe Notify do ...@@ -253,6 +265,10 @@ describe Notify do
it 'contains the target branch for the merge request' do it 'contains the target branch for the merge request' do
should have_body_text /#{merge_request.target_branch}/ should have_body_text /#{merge_request.target_branch}/
end end
it 'has the correct message-id set' do
should have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>"
end
end end
describe 'that are new with a description' do describe 'that are new with a description' do
...@@ -275,7 +291,7 @@ describe Notify do ...@@ -275,7 +291,7 @@ describe Notify do
end end
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains the name of the previous assignee' do it 'contains the name of the previous assignee' do
...@@ -303,7 +319,7 @@ describe Notify do ...@@ -303,7 +319,7 @@ describe Notify do
end end
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains the new status' do it 'contains the new status' do
...@@ -313,6 +329,10 @@ describe Notify do ...@@ -313,6 +329,10 @@ describe Notify do
it 'contains a link to the merge request' do it 'contains a link to the merge request' do
should have_body_text /#{project_merge_request_path project, merge_request}/ should have_body_text /#{project_merge_request_path project, merge_request}/
end end
it 'has the correct reference set' do
should have_header 'References', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>"
end
end end
end end
end end
...@@ -426,7 +446,7 @@ describe Notify do ...@@ -426,7 +446,7 @@ describe Notify do
it_behaves_like 'a note email' it_behaves_like 'a note email'
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains a link to the merge request note' do it 'contains a link to the merge request note' do
......
...@@ -39,6 +39,17 @@ describe API, api: true do ...@@ -39,6 +39,17 @@ describe API, api: true do
end end
describe ".current_user" do describe ".current_user" do
it "should return nil for an invalid token" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
current_user.should be_nil
end
it "should return nil for a user without access" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
Gitlab::UserAccess.stub(allowed?: false)
current_user.should be_nil
end
it "should leave user as is when sudo not specified" do it "should leave user as is when sudo not specified" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
current_user.should == user current_user.should == user
......
...@@ -183,11 +183,33 @@ describe API::API, api: true do ...@@ -183,11 +183,33 @@ describe API::API, api: true do
end end
end end
describe "PUT /projects/:id/merge_request/:merge_request_id to merge MR" do describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
it "should return merge_request" do it "should return merge_request in case of success" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "merge" MergeRequest.any_instance.stub(can_be_merged?: true, automerge!: true)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
response.status.should == 200 response.status.should == 200
json_response['state'].should == 'merged' end
it "should return 405 if branch can't be merged" do
MergeRequest.any_instance.stub(can_be_merged?: false)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
response.status.should == 405
json_response['message'].should == 'Branch cannot be merged'
end
it "should return 405 if merge_request is not open" do
merge_request.close
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
response.status.should == 405
json_response['message'].should == 'Method Not Allowed'
end
it "should return 401 if user has no permissions to merge" do
user2 = create(:user)
project.team << [user2, :reporter]
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user2)
response.status.should == 401
json_response['message'].should == '401 Unauthorized'
end end
end end
......
...@@ -14,6 +14,12 @@ describe API::API, api: true do ...@@ -14,6 +14,12 @@ describe API::API, api: true do
let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } let(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
let(:issue_with_labels) { create(:issue, author: user, assignee: user, project: project, :label_list => "label1, label2") } let(:issue_with_labels) { create(:issue, author: user, assignee: user, project: project, :label_list => "label1, label2") }
let(:merge_request_with_labels) do
create(:merge_request, :simple, author: user, assignee: user,
source_project: project, target_project: project, title: 'Test',
label_list: 'label3, label4')
end
describe "GET /projects" do describe "GET /projects" do
before { project } before { project }
...@@ -634,15 +640,45 @@ describe API::API, api: true do ...@@ -634,15 +640,45 @@ describe API::API, api: true do
end end
end end
describe "GET /projects/:id/labels" do describe 'GET /projects/:id/labels' do
before { issue_with_labels } context 'with an issue' do
before { issue_with_labels }
it "should return project labels" do it 'should return project labels' do
get api("/projects/#{project.id}/labels", user) get api("/projects/#{project.id}/labels", user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.first['name'].should == issue_with_labels.labels.first.name json_response.first['name'].should == issue_with_labels.labels.first.name
json_response.last['name'].should == issue_with_labels.labels.last.name json_response.last['name'].should == issue_with_labels.labels.last.name
end
end
context 'with a merge request' do
before { merge_request_with_labels }
it 'should return project labels' do
get api("/projects/#{project.id}/labels", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == merge_request_with_labels.labels.first.name
json_response.last['name'].should == merge_request_with_labels.labels.last.name
end
end
context 'with an issue and a merge request' do
before do
issue_with_labels
merge_request_with_labels
end
it 'should return project labels from both' do
get api("/projects/#{project.id}/labels", user)
response.status.should == 200
json_response.should be_an Array
all_labels = issue_with_labels.labels.map(&:name).to_a
.concat(merge_request_with_labels.labels.map(&:name).to_a)
json_response.map { |e| e['name'] }.should =~ all_labels
end
end end
end end
end end
...@@ -213,7 +213,7 @@ describe Projects::RefsController, "routing" do ...@@ -213,7 +213,7 @@ describe Projects::RefsController, "routing" do
end end
# diffs_project_merge_request GET /:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs # diffs_project_merge_request GET /:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs
# automerge_project_merge_request GET /:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge # automerge_project_merge_request POST /:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge
# automerge_check_project_merge_request GET /:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check # automerge_check_project_merge_request GET /:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check
# branch_from_project_merge_requests GET /:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from # branch_from_project_merge_requests GET /:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from
# branch_to_project_merge_requests GET /:project_id/merge_requests/branch_to(.:format) projects/merge_requests#branch_to # branch_to_project_merge_requests GET /:project_id/merge_requests/branch_to(.:format) projects/merge_requests#branch_to
...@@ -230,7 +230,10 @@ describe Projects::MergeRequestsController, "routing" do ...@@ -230,7 +230,10 @@ describe Projects::MergeRequestsController, "routing" do
end end
it "to #automerge" do it "to #automerge" do
get("/gitlab/gitlabhq/merge_requests/1/automerge").should route_to('projects/merge_requests#automerge', project_id: 'gitlab/gitlabhq', id: '1') post('/gitlab/gitlabhq/merge_requests/1/automerge').should route_to(
'projects/merge_requests#automerge',
project_id: 'gitlab/gitlabhq', id: '1'
)
end end
it "to #automerge_check" do it "to #automerge_check" do
......
...@@ -8,10 +8,10 @@ describe SystemHooksService do ...@@ -8,10 +8,10 @@ describe SystemHooksService do
context 'event data' do context 'event data' do
it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) } it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) }
it { event_data(user, :destroy).should include(:event_name, :name, :created_at, :email, :user_id) } it { event_data(user, :destroy).should include(:event_name, :name, :created_at, :email, :user_id) }
it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) } it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) } it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
it { event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) } it { event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access, :project_visibility) }
it { event_data(users_project, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) } it { event_data(users_project, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access, :project_visibility) }
end end
context 'event names' do context 'event names' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment