diff --git a/.pkgr.yml b/.pkgr.yml
new file mode 100644
index 0000000000..09cb83783d
--- /dev/null
+++ b/.pkgr.yml
@@ -0,0 +1,19 @@
+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
diff --git a/CHANGELOG b/CHANGELOG
index 185e5df46c..61c6b94b79 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,18 @@
+v 6.9.0
+ - Store Rails cache data in the Redis `cache:gitlab` namespace
+ - Adjust MySQL limits for existing installations
+ - Add db index on project_id+iid column. This prevents duplicate on iid (During migration duplicates will be removed)
+ - Markdown preview or diff during editing via web editor (Evgeniy Sokovikov)
+ - Give the Rails cache its own Redis namespace
+ - Add ability to set different ssh host, if different from http/https
+ - Fix syntax highlighting for code comments blocks
+ - Improve comments loading logic
+ - 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 wiki backup skip bug
+
v 6.8.1
- Bump required gitlab-shell version to 1.9.3
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 290804e618..780db547f8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -107,9 +107,10 @@ For examples of feedback on merge requests please look at already [closed merge
## Style guides
1. [Ruby](https://github.com/bbatsov/ruby-style-guide)
-2. [Rails](https://github.com/bbatsov/rails-style-guide)
-3. [Formatting](https://github.com/thoughtbot/guides/tree/master/style#formatting)
-4. [Naming](https://github.com/thoughtbot/guides/tree/master/style#naming)
-8. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing)
-7. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
-9. [Shell commands](doc/development/shell_commands.md)
+1. [Rails](https://github.com/bbatsov/rails-style-guide)
+1. [Formatting](https://github.com/thoughtbot/guides/tree/master/style#formatting)
+1. [Naming](https://github.com/thoughtbot/guides/tree/master/style#naming)
+1. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing)
+1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
+1. [Shell commands](doc/development/shell_commands.md)
+1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
diff --git a/Gemfile b/Gemfile
index 07b244b52a..51418f57d4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -72,6 +72,7 @@ gem "carrierwave"
# for aws storage
gem "fog", "~> 1.14", group: :aws
+gem "unf", group: :aws
# Authorization
gem "six"
@@ -83,6 +84,9 @@ gem "seed-fu"
gem "redcarpet", "~> 2.2.2"
gem "github-markup"
+# Diffs
+gem 'diffy', '~> 3.0.3'
+
# Asciidoc to HTML
gem "asciidoctor"
@@ -158,8 +162,8 @@ gem 'jquery-turbolinks'
gem 'select2-rails'
gem 'jquery-atwho-rails', "~> 0.3.3"
-gem "jquery-rails", "2.1.3"
-gem "jquery-ui-rails", "2.0.2"
+gem "jquery-rails"
+gem "jquery-ui-rails"
gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index d0f9f15d1d..07a611a2fc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -100,6 +100,7 @@ GEM
devise-async (0.8.0)
devise (>= 2.2, < 3.2)
diff-lcs (1.2.5)
+ diffy (3.0.3)
docile (1.1.1)
dotenv (0.9.0)
email_spec (1.5.0)
@@ -161,7 +162,7 @@ GEM
multi_json
gitlab-grack (2.0.0.pre)
rack (~> 1.5.1)
- gitlab-grit (2.6.5)
+ gitlab-grit (2.6.6)
charlock_holmes (~> 0.6)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
@@ -246,15 +247,14 @@ GEM
rake
jasmine-core (2.0.0.rc5)
jquery-atwho-rails (0.3.3)
- jquery-rails (2.1.3)
- railties (>= 3.1.0, < 5.0)
- thor (~> 0.14)
+ jquery-rails (3.1.0)
+ railties (>= 3.0, < 5.0)
+ thor (>= 0.14, < 2.0)
jquery-turbolinks (2.0.1)
railties (>= 3.1.0)
turbolinks
- jquery-ui-rails (2.0.2)
- jquery-rails
- railties (>= 3.1.0)
+ jquery-ui-rails (4.2.1)
+ railties (>= 3.2.16)
json (1.8.1)
jwt (0.1.8)
multi_json (>= 1.5)
@@ -530,6 +530,9 @@ GEM
execjs (>= 0.3.0)
json (>= 1.8.0)
underscore-rails (1.4.4)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.6)
unicorn (4.6.3)
kgio (~> 2.6)
rack
@@ -573,6 +576,7 @@ DEPENDENCIES
default_value_for (~> 3.0.0)
devise (= 3.0.4)
devise-async (= 0.8.0)
+ diffy (~> 3.0.3)
email_spec
email_validator (~> 1.4.0)
enumerize
@@ -602,9 +606,9 @@ DEPENDENCIES
httparty
jasmine (= 2.0.0.rc5)
jquery-atwho-rails (~> 0.3.3)
- jquery-rails (= 2.1.3)
+ jquery-rails
jquery-turbolinks
- jquery-ui-rails (= 2.0.2)
+ jquery-ui-rails
kaminari (~> 0.15.1)
launchy
letter_opener
@@ -659,6 +663,7 @@ DEPENDENCIES
turbolinks
uglifier
underscore-rails (~> 1.4.4)
+ unf
unicorn (~> 4.6.3)
unicorn-worker-killer
version_sorter
diff --git a/README.md b/README.md
index 3f3453a267..c7c979c0ca 100644
--- a/README.md
+++ b/README.md
@@ -29,11 +29,11 @@
* [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 Enterprise Edition](https://www.gitlab.com/gitlab-ce/) offers additional features that are useful for larger organizations (100+ users).
+* [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) offers additional features aimed at larger organizations.
* [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/) and [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) 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) for GitLab.
### Requirements
diff --git a/VERSION b/VERSION
index 4dd7f4527b..13530c8855 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-6.8.0-ee
+6.9.0.pre-ee
diff --git a/app/assets/images/ui-icons_222222_256x240.png b/app/assets/images/ui-icons_222222_256x240.png
deleted file mode 100644
index 8bc06cbf03..0000000000
Binary files a/app/assets/images/ui-icons_222222_256x240.png and /dev/null differ
diff --git a/app/assets/images/ui-icons_454545_256x240.png b/app/assets/images/ui-icons_454545_256x240.png
deleted file mode 100644
index cfd1eaffaa..0000000000
Binary files a/app/assets/images/ui-icons_454545_256x240.png and /dev/null differ
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 46d6db0f05..b61d9875e0 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -59,7 +59,7 @@ class Dispatcher
initHighlight: ->
$('.highlight pre code').each (i, e) ->
- hljs.highlightBlock(e)
$(e).html($.map($(e).html().split("\n"), (line, i) ->
- "
" + line + "
"
+ "" + line + ""
).join("\n"))
+ hljs.highlightBlock(e)
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index d200d962ca..47989010d1 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -1,10 +1,11 @@
class Notes
@interval: null
- constructor: (notes_url, note_ids) ->
+ constructor: (notes_url, note_ids, last_fetched_at) ->
@notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids
+ @last_fetched_at = last_fetched_at
@initRefresh()
@setupMainTargetNoteForm()
@cleanBinding()
@@ -49,6 +50,15 @@ class Notes
# hide diff note form
$(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm
+ # fetch notes when tab becomes visible
+ $(document).on "visibilitychange", @visibilityChange
+
+ @notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
+ $(document).on('keypress', @notes_forms, (e)->
+ if event.keyCode == 10 || (event.ctrlKey && event.keyCode == 13)
+ $(@).parents('form').submit()
+ )
+
cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form"
@@ -62,6 +72,8 @@ class Notes
$(document).off "click", ".js-choose-note-attachment-button"
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
+ $(document).off "visibilitychange"
+ $(document).off "keypress", @notes_forms
initRefresh: ->
@@ -71,14 +83,16 @@ class Notes
, 15000
refresh: ->
- @getContent()
+ @getContent() unless document.hidden
getContent: ->
$.ajax
url: @notes_url
+ data: "last_fetched_at=" + @last_fetched_at
dataType: "json"
success: (data) =>
notes = data.notes
+ @last_fetched_at = data.last_fetched_at
$.each notes, (i, note) =>
@renderNote(note)
@@ -450,4 +464,10 @@ class Notes
filename = $(this).val().replace(/^.*[\\\/]/, "")
form.find(".js-attachment-filename").text filename
+ ###
+ Called when the tab visibility changes
+ ###
+ visibilityChange: =>
+ @refresh()
+
@Notes = Notes
diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee
index 03fad41c49..b0e39610fe 100644
--- a/app/assets/javascripts/project_users_select.js.coffee
+++ b/app/assets/javascripts/project_users_select.js.coffee
@@ -1,7 +1,7 @@
@projectUsersSelect =
init: ->
$('.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
placeholder: $(select).data('placeholder') || "Search for a user"
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 2ce09a6553..c53873f95a 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -12,10 +12,7 @@
*= require nprogress-bootstrap
*/
-@import "main/variables.scss";
-@import "main/mixins.scss";
-@import "main/fonts.scss";
-@import "main/layout.scss";
+@import "main/*";
/**
* Customized Twitter bootstrap
@@ -31,65 +28,22 @@
/**
* Generic css (forms, nav etc):
*/
-@import "generic/avatar.scss";
-@import "generic/common.scss";
-@import "generic/typography.scss";
-@import "generic/buttons.scss";
-@import "generic/blocks.scss";
-@import "generic/ui_box.scss";
-@import "generic/issue_box.scss";
-@import "generic/files.scss";
-@import "generic/lists.scss";
-@import "generic/flash.scss";
-@import "generic/forms.scss";
-@import "generic/selects.scss";
-@import "generic/highlight.scss";
-@import "generic/jquery.scss";
+@import "generic/*";
/**
* Page specific styles (issues, projects etc):
*/
-@import "sections/header.scss";
-@import "sections/nav.scss";
-@import "sections/commits.scss";
-@import "sections/diff.scss";
-@import "sections/issues.scss";
-@import "sections/projects.scss";
-@import "sections/snippets.scss";
-@import "sections/votes.scss";
-@import "sections/merge_requests.scss";
-@import "sections/graph.scss";
-@import "sections/events.scss";
-@import "sections/themes.scss";
-@import "sections/tree.scss";
-@import "sections/notes.scss";
-@import "sections/profile.scss";
-@import "sections/login.scss";
-@import "sections/editor.scss";
-@import "sections/admin.scss";
-@import "sections/wiki.scss";
-@import "sections/wall.scss";
-@import "sections/dashboard.scss";
-@import "sections/stat_graph.scss";
-@import "sections/groups.scss";
-@import "sections/appearances.scss";
+@import "sections/*";
/**
* Code highlight
*/
-@import "highlight/white.scss";
-@import "highlight/dark.scss";
-@import "highlight/solarized_dark.scss";
-@import "highlight/monokai.scss";
+@import "highlight/*";
/**
* UI themes:
*/
-@import "themes/ui_basic.scss";
-@import "themes/ui_mars.scss";
-@import "themes/ui_modern.scss";
-@import "themes/ui_gray.scss";
-@import "themes/ui_color.scss";
+@import "themes/*";
/**
* Styles for JS behaviors.
diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss
index 12559f7605..6418f24d97 100644
--- a/app/assets/stylesheets/generic/files.scss
+++ b/app/assets/stylesheets/generic/files.scss
@@ -26,6 +26,10 @@
margin-top: -5px;
}
+ .left-options {
+ margin-top: -3px;
+ }
+
.file_name {
color: $style_color;
font-size: 14px;
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index 3db4d908d9..d4d3361bc7 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -12,6 +12,7 @@
margin:20px 0;
background: #FFF;
border: 1px solid #EEE;
+ @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
&.issue-box-closed {
border-color: #DA4E49;
@@ -70,7 +71,6 @@
}
.state {
- height: 34px;
border-bottom: 1px solid #DDD;
line-height: 32px;
}
@@ -89,6 +89,18 @@
border: none;
border-top: 1px solid #eee;
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 {
@@ -106,7 +118,11 @@
padding: 1px 25px;
text-align: center;
text-shadow: none;
- margin-right: 20px;
display: inline-block;
+ line-height: 34px;
+ }
+
+ .creator {
+ padding: 2px 15px;
}
}
diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/generic/jquery.scss
index 4a9341e8f5..6b29accb31 100644
--- a/app/assets/stylesheets/generic/jquery.scss
+++ b/app/assets/stylesheets/generic/jquery.scss
@@ -8,7 +8,7 @@
width: 270px;
.ui-datepicker-header {
- background: #EEE;
+ background: #FFF;
border-color: #DDD;
}
@@ -19,20 +19,37 @@
}
&.ui-autocomplete {
- @include border-radius(0px);
border-color: #DDD;
padding: 0;
+ margin-top: 2px;
+ z-index: 1001;
.ui-menu-item a {
- color: #777;
-
- &:hover {
- background: $hover;
- border-color: $primary_color;
- @include border-radius(0px);
- color: #333;
- }
+ padding: 4px 10px;
}
}
-}
+ .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;
+ }
+}
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index a441955173..8cc72d7f07 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -47,7 +47,7 @@ a {
text-decoration: underline;
}
- &.dark {
+ &.darken {
color: $style_color;
}
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index fcc7374d0d..147c759ecb 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -41,31 +41,6 @@
* Prefilled mixins
* 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 {
@include box-shadow(0 0 3px #ddd);
diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss
index eb272f20f4..64e669ac2b 100644
--- a/app/assets/stylesheets/sections/diff.scss
+++ b/app/assets/stylesheets/sections/diff.scss
@@ -63,26 +63,14 @@
}
}
- .text-file-parallel div {
- display: inline-block;
- padding-bottom: 16px;
- }
- .diff-side {
- overflow-x: scroll;
- width: 508px;
- height: 700px;
- }
- .diff-side.diff-side-left{
- overflow-y:hidden;
- }
- .diff-side table, td.diff-middle table {
- height: 700px;
- }
- .diff-middle {
- width: 114px;
- vertical-align: top;
- height: 700px;
- overflow: hidden
+ tr.line_holder.parallel{
+ .old_line, .new_line, .diff_line {
+ min-width: 50px;
+ }
+
+ td.line_content.parallel{
+ width: 50%;
+ }
}
.old_line, .new_line, .diff_line {
diff --git a/app/assets/stylesheets/sections/graph.scss b/app/assets/stylesheets/sections/graph.scss
index 1e22d161bf..8a337a5e20 100644
--- a/app/assets/stylesheets/sections/graph.scss
+++ b/app/assets/stylesheets/sections/graph.scss
@@ -1,17 +1,16 @@
.project-network {
- border: 1px solid #aaa;
- padding: 1px;
+ border: 1px solid #CCC;
.tip {
color: #888;
font-size: 14px;
padding: 10px;
border-bottom: 1px solid #bbb;
- @include bg-gray-gradient;
+ background: #EEE;
}
.network-graph {
- background: #f1f1f1;
+ background: #FFF;
height: 500px;
overflow-y: scroll;
overflow-x: hidden;
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index d4f8c8108a..02c9123178 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -45,14 +45,6 @@
padding: 6px 10px;
border: 1px solid #ccc;
@include border-radius(4px);
-
-
- input.check_all_issues {
- padding: 0;
- margin: 0;
- position: relative;
- top: 3px;
- }
}
.issues_content {
@@ -143,3 +135,36 @@ form.edit-issue {
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;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss
index 790496a1a5..5dcfc449b4 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/sections/merge_requests.scss
@@ -31,7 +31,6 @@
.mr_source_commit,
.mr_target_commit {
- margin-top: 10px;
.commit {
margin: 0;
padding: 2px 0;
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index c9c7b6ecce..7e56781f56 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -139,6 +139,7 @@ ul.notes {
background-color: #fff;
border-width: 1px 0;
padding-top: 0;
+ vertical-align: top;
li {
padding: 5px;
diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss
index 13f811e01a..d683e33e1f 100644
--- a/app/assets/stylesheets/sections/votes.scss
+++ b/app/assets/stylesheets/sections/votes.scss
@@ -40,4 +40,10 @@
.votes-holder {
float: right;
width: 250px;
+
+ @media (max-width: $screen-xs-max) {
+ width: 100%;
+ margin-top: 5px;
+ margin-bottom: 10px;
+ }
}
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 13a7bdcf34..92ef596337 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -12,6 +12,7 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.search(params[:name]) if params[:name].present?
+ @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
end
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
index ff5206b6fa..be611892bb 100644
--- a/app/controllers/projects/edit_tree_controller.rb
+++ b/app/controllers/projects/edit_tree_controller.rb
@@ -26,6 +26,18 @@ class Projects::EditTreeController < Projects::BaseTreeController
end
end
+ def preview
+ @content = params[:content]
+ #FIXME workaround https://github.com/gitlabhq/gitlabhq/issues/5936
+ @content += "\n" if @blob.data.end_with?("\n")
+
+ diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
+ include_diff_info: true)
+ @diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/))
+
+ render layout: false
+ end
+
private
def blob
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 745da9c49e..d8551db7b0 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -62,11 +62,27 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.source_project = @project unless @merge_request.source_project
@merge_request.target_project ||= (@project.forked_from_project || @project)
@target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
-
@merge_request.target_branch ||= @merge_request.target_project.default_branch
-
@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
def edit
@@ -80,7 +96,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute
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
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 85d042a89b..b5b0446b43 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController
before_filter :authorize_admin_note!, only: [:update, :destroy]
def index
+ current_fetched_at = Time.now.to_i
@notes = NotesFinder.new.execute(project, current_user, params)
- notes_json = { notes: [] }
+ notes_json = { notes: [], last_fetched_at: current_fetched_at }
@notes.each do |note|
notes_json[:notes] << {
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index bcd9e0d521..496064c9a6 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -12,9 +12,22 @@ class Projects::WikisController < Projects::ApplicationController
def show
@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
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
return render('empty') unless can?(current_user, :write_wiki, @project)
@page = WikiPage.new(@project_wiki)
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 384316e14b..ea055694cd 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -1,9 +1,13 @@
class NotesFinder
+ FETCH_OVERLAP = 5.seconds
+
def execute(project, current_user, params)
target_type = params[:target_type]
target_id = params[:target_id]
+ # Default to 0 to remain compatible with old clients
+ last_fetched_at = Time.at(params.fetch(:last_fetched_at, 0).to_i)
- case target_type
+ notes = case target_type
when "commit"
project.notes.for_commit_id(target_id).not_inline.fresh
when "issue"
@@ -12,6 +16,11 @@ class NotesFinder
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
when "snippet"
project.snippets.find(target_id).notes.fresh
+ else
+ raise 'invalid target_type'
end
+
+ # Use overlapping intervals to avoid worrying about race conditions
+ notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP)
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 00ee734705..d8ba2727cc 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -75,7 +75,7 @@ module ApplicationHelper
else
gravatar_url = request.ssl? || gitlab_config.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
user_email.strip!
- sprintf gravatar_url, hash: Digest::MD5.hexdigest(user_email.downcase), size: size
+ sprintf gravatar_url, hash: Digest::MD5.hexdigest(user_email.downcase), size: size, email: user_email
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index c6e4f574b6..c4abdbdabc 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -16,9 +16,10 @@ module CommitsHelper
end
def each_diff_line(diff, index)
- Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
- yield(full_line, type, line_code, line_new, line_old)
- end
+ Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
+ .each do |full_line, type, line_code, line_new, line_old|
+ yield(full_line, type, line_code, line_new, line_old)
+ end
end
def each_diff_line_near(diff, index, expected_line_code)
@@ -116,7 +117,7 @@ module CommitsHelper
added_lines[line_new] = { line_code: line_code, type: type, line: line }
end
end
- max_length = old_file ? old_file.sloc + added_lines.length : file.sloc
+ max_length = old_file ? [old_file.loc, file.loc].max : file.loc
offset1 = 0
offset2 = 0
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 95f0eff58b..7c58908165 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -82,7 +82,7 @@ module IssuesHelper
end
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
def issue_box_class(item)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 9bb3efc41d..ef0460f872 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -163,7 +163,7 @@ module ProjectsHelper
end
def repository_size(project = nil)
- "#{(project || @project).repository.size} MB"
+ "#{(project || @project).repository_size} MB"
rescue
# In order to prevent 500 error
# when application cannot allocate memory
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index a1fe4488ae..ab24367c45 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -14,7 +14,7 @@ module SelectsHelper
css_class << (opts[:class] || '')
value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Select user'
-
- hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder)
+ project_id = opts[:project_id] || @project.id
+ hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 50501dffef..f39d0081dc 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -91,4 +91,12 @@ module TreeHelper
def leave_edit_message
"Leave edit mode?\nAll unsaved changes will be lost."
end
+
+ def editing_preview_title(filename)
+ if gitlab_markdown?(filename) || markup?(filename)
+ 'Preview'
+ else
+ 'Diff'
+ end
+ end
end
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index a97d55f1b5..1130969a26 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -6,7 +6,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(@merge_request.author_id),
to: recipient(recipient_id),
- subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
+ subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
@@ -16,7 +16,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
- subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
+ subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@@ -26,7 +26,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
- subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
+ subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@@ -35,7 +35,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
- subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
+ subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
end
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index ccbdadf010..2a877bc159 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -27,7 +27,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
- subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
+ subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def note_wall_email(recipient_id, note_id)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 8c885b70a4..134a8c8dd4 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -253,6 +253,14 @@ class MergeRequest < ActiveRecord::Base
end
end
+ def target_project_namespace
+ if target_project && target_project.namespace
+ target_project.namespace.path
+ else
+ "(removed)"
+ end
+ end
+
def source_branch_exists?
return false unless self.source_project
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 0684461add..7dce71a677 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -86,7 +86,7 @@ class MergeRequestDiff < ActiveRecord::Base
# between target and source branches
def unmerged_commits
commits = if merge_request.for_fork?
- Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between
+ compare_action.commits
else
repository.commits_between(target_branch, source_branch)
end
@@ -150,7 +150,7 @@ class MergeRequestDiff < ActiveRecord::Base
# between target and source branches
def unmerged_diffs
diffs = if merge_request.for_fork?
- Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite
+ compare_action.diffs
else
Gitlab::Git::Diff.between(repository, source_branch, target_branch)
end
@@ -165,4 +165,16 @@ class MergeRequestDiff < ActiveRecord::Base
def repository
merge_request.target_project.repository
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
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 6a2ca76703..39ab0b536a 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -25,6 +25,7 @@ class Milestone < ActiveRecord::Base
scope :active, -> { with_state(:active) }
scope :closed, -> { with_state(:closed) }
+ scope :of_projects, ->(ids) { where(project_id: ids) }
validates :title, presence: true
validates :project, presence: true
diff --git a/app/models/note.rb b/app/models/note.rb
index 6f7afcd1f9..cee10ec90d 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -184,9 +184,10 @@ class Note < ActiveRecord::Base
return @diff_line if @diff_line
if diff
- Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
- @diff_line = full_line if line_code == self.line_code
- end
+ Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
+ .each do |full_line, type, line_code, line_new, line_old|
+ @diff_line = full_line if line_code == self.line_code
+ end
end
@diff_line
diff --git a/app/models/project.rb b/app/models/project.rb
index bd7b43723d..1da24ee649 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -207,6 +207,7 @@ class Project < ActiveRecord::Base
when 'oldest' then reorder('projects.created_at ASC')
when 'recently_updated' then reorder('projects.updated_at DESC')
when 'last_updated' then reorder('projects.updated_at ASC')
+ when 'largest_repository' then reorder('projects.repository_size DESC')
else reorder("namespaces.path, projects.name ASC")
end
end
@@ -566,4 +567,8 @@ class Project < ActiveRecord::Base
def forked_from?(project)
forked? && project == forked_from_project
end
+
+ def update_repository_size
+ update_attribute(:repository_size, repository.size)
+ end
end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 163302a18f..08a5278247 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -64,7 +64,8 @@ class ProjectWiki
#
# Returns an initialized WikiPage instance or 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)
else
nil
@@ -90,6 +91,12 @@ class ProjectWiki
wiki.delete_page(page, commit_details(:deleted, message, page.title))
end
+ def page_title_and_dir(title)
+ title_array = title.split("/")
+ title = title_array.pop
+ [title.gsub(/\.[^.]*$/, ""), title_array.join("/")]
+ end
+
private
def create_repo!
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 76f311ed0b..b8a0a9eb58 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -175,14 +175,24 @@ class WikiPage
end
def save(method, *args)
- if valid? && wiki.send(method, *args)
- @page = wiki.wiki.paged(title)
+ project_wiki = wiki
+ 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
@persisted = true
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
end
@persisted
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 351b446457..715b569075 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -25,6 +25,7 @@ class GitPushService
project.ensure_satellite_exists
project.repository.expire_cache
+ project.update_repository_size
if push_to_existing_branch?(ref, oldrev)
project.update_merge_requests(oldrev, newrev, ref, @user)
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index f28dadfb65..c58ca2c9a3 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -22,12 +22,12 @@
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
= f.text_field :color, placeholder: "#AA33EE", class: "form-control"
- .light Hex values as 3 double digit numbers, starting with a # sign.
+ .light 6 character hex values starting with a # sign.
.form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label'
.col-sm-10
= f.text_field :font, placeholder: "#224466", class: "form-control"
- .light Hex values as 3 double digit numbers, starting with a # sign.
+ .light 6 character hex values starting with a # sign.
.form-group
= f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 296094ab29..51ad702154 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -32,6 +32,7 @@
= visibility_level_icon(level)
= label
.form-actions
+ = hidden_field_tag :sort, params[:sort]
= submit_tag "Search", class: "btn submit btn-primary"
= link_to "Reset", admin_projects_path, class: "btn"
@@ -40,6 +41,28 @@
.title
Projects (#{@projects.total_count})
.pull-right
+ .dropdown.inline
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %span.light sort:
+ - if @sort.present?
+ = @sort.humanize
+ - else
+ Name
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to admin_projects_path(sort: nil) do
+ Name
+ = link_to admin_projects_path(sort: 'newest') do
+ Newest
+ = link_to admin_projects_path(sort: 'oldest') do
+ Oldest
+ = link_to admin_projects_path(sort: 'recently_updated') do
+ Recently updated
+ = link_to admin_projects_path(sort: 'last_updated') do
+ Last updated
+ = link_to admin_projects_path(sort: 'largest_repository') do
+ Largest repository
= link_to 'New Project', new_project_path, class: "btn btn-new"
%ul.well-list
- @projects.each do |project|
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index d8001fd76d..fba56b5dc3 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -19,6 +19,10 @@
%li.visible-sm.visible-xs
= link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do
%i.icon-search
+ %li
+ = link_to help_path, title: 'Help', class: 'has_bottom_tooltip',
+ 'data-original-title' => 'Help' do
+ %i.icon-question-sign
%li
= link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
%i.icon-globe
@@ -39,6 +43,6 @@
%li
= link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
%i.icon-signout
- %li
+ %li.hidden-xs
= link_to current_user, class: "profile-pic", id: 'profile-pic' do
= image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml
index 809d46f31b..574e8bfef2 100644
--- a/app/views/notify/closed_merge_request_email.html.haml
+++ b/app/views/notify/closed_merge_request_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}"
+ = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml
index ee434ec8cb..d6b76e906c 100644
--- a/app/views/notify/closed_merge_request_email.text.haml
+++ b/app/views/notify/closed_merge_request_email.text.haml
@@ -1,4 +1,4 @@
-= "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)}
diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml
index 0c62d439ae..6762fae7f6 100644
--- a/app/views/notify/merged_merge_request_email.html.haml
+++ b/app/views/notify/merged_merge_request_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Merge Request !#{@merge_request.iid} was merged"
+ = "Merge Request ##{@merge_request.iid} was merged"
diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml
index 550f677fed..360da60bc3 100644
--- a/app/views/notify/merged_merge_request_email.text.haml
+++ b/app/views/notify/merged_merge_request_email.text.haml
@@ -1,4 +1,4 @@
-= "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)}
diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml
index b8d7fbeb04..a15b8efe1f 100644
--- a/app/views/notify/repository_push_email.text.haml
+++ b/app/views/notify/repository_push_email.text.haml
@@ -17,7 +17,7 @@ Changes:
- else
= diff.new_path || diff.old_path
\=====================================
- = diff.diff
+ != diff.diff
\
- if @compare.timeout
Huge diff. To prevent performance issues it was hidden
diff --git a/app/views/projects/commits/_parallel_view.html.haml b/app/views/projects/commits/_parallel_view.html.haml
index 5b60ab80ba..80f5be98f2 100644
--- a/app/views/projects/commits/_parallel_view.html.haml
+++ b/app/views/projects/commits/_parallel_view.html.haml
@@ -2,54 +2,37 @@
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length
-%div.text-file-parallel
- %div.diff-side.diff-side-left
- %table
- - old_lines.each do |line|
+%div.text-file
+ %table
+ - num_lines.times do |index|
+ - 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
- %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
+ -# 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
- %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
+ - 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
- %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());
- });
diff --git a/app/views/projects/edit_tree/_diff.html.haml b/app/views/projects/edit_tree/_diff.html.haml
new file mode 100644
index 0000000000..cf044feb9a
--- /dev/null
+++ b/app/views/projects/edit_tree/_diff.html.haml
@@ -0,0 +1,13 @@
+%table.text-file
+ - each_diff_line(diff, 1) do |line, type, line_code, line_new, line_old, raw_line|
+ %tr.line_holder{ id: line_code, class: "#{type}" }
+ - if type == "match"
+ %td.old_line= "..."
+ %td.new_line= "..."
+ %td.line_content.matched= line
+ - else
+ %td.old_line
+ = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code
+ %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code
+ %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
+
diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/edit_tree/preview.html.haml
new file mode 100644
index 0000000000..fc6d3bfbc2
--- /dev/null
+++ b/app/views/projects/edit_tree/preview.html.haml
@@ -0,0 +1,26 @@
+.diff-file
+ .diff-content
+ - if gitlab_markdown?(@blob.name)
+ .file-content.wiki
+ = preserve do
+ = markdown(@content)
+ - elsif markup?(@blob.name)
+ .file-content.wiki
+ = raw GitHub::Markup.render(@blob.name, @content)
+ - else
+ .file-content.code
+ - unless @diff.empty?
+ %table.text-file
+ - @diff.each do |line, type, line_code, line_new, line_old, raw_line|
+ %tr.line_holder{ id: line_code, class: "#{type}" }
+ - if type == "match"
+ %td.old_line= "..."
+ %td.new_line= "..."
+ %td.line_content.matched= line
+ - else
+ %td.old_line
+ = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code
+ %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code
+ %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
+ - else
+ %p.nothing_here_message No changes.
diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml
index 3f2e98f3a7..48babb43aa 100644
--- a/app/views/projects/edit_tree/show.html.haml
+++ b/app/views/projects/edit_tree/show.html.haml
@@ -1,8 +1,11 @@
%h3.page-title Edit mode
.file-editor
= form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do
- .file-holder
+ .file-holder.file
.file-title
+ .btn-group.js-edit-mode.left-options
+ = link_to 'Edit', '#editor', class: 'active hover btn btn-tiny'
+ = link_to editing_preview_title(@blob.name), '#preview', class: 'btn btn-tiny', 'data-preview-url' => preview_project_edit_tree_path(@project, @id)
%i.icon-file
%span.file_name
= @path
@@ -13,7 +16,8 @@
.btn-group.tree-btn-group
= link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message }
.file-content.code
- %pre#editor= @blob.data
+ %pre.js-edit-mode-pane#editor= @blob.data
+ .js-edit-mode-pane#preview.hide
.form-group.commit_message-group
= label_tag 'commit_message', class: "control-label" do
@@ -45,3 +49,28 @@
$("#file-content").val(editor.getValue());
$(".file-editor form").submit();
});
+
+ var editModePanes = $('.js-edit-mode-pane'),
+ editModeLinks = $('.js-edit-mode a');
+
+ editModeLinks.click(function(event) {
+ event.preventDefault();
+
+ var currentLink = $(this),
+ paneId = currentLink.attr('href'),
+ currentPane = editModePanes.filter(paneId);
+
+ editModeLinks.removeClass('active hover');
+ currentLink.addClass('active hover');
+ editModePanes.hide();
+
+ if (paneId == '#preview') {
+ $.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) {
+ currentPane.empty().append(response);
+ currentPane.fadeIn(200);
+ })
+ } else {
+ currentPane.fadeIn(200);
+ editor.focus()
+ }
+ })
diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml
index aae101cf40..425dcb45dd 100644
--- a/app/views/projects/issues/_issue_context.html.haml
+++ b/app/views/projects/issues/_issue_context.html.haml
@@ -1,22 +1,24 @@
= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
- %strong.append-right-10
- Assignee:
+ .row
+ .col-md-6
+ %strong.append-right-10
+ Assignee:
- - 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)
- - elsif issue.assignee
- = link_to_member(@project, @issue.assignee)
- - else
- None
+ - 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)
+ - elsif issue.assignee
+ = link_to_member(@project, @issue.assignee)
+ - else
+ None
- .pull-right
- %strong.append-right-10
- Milestone:
- - if can?(current_user, :modify_issue, @issue)
- = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'})
- = hidden_field_tag :issue_context
- = f.submit class: 'btn'
- - elsif issue.milestone
- = link_to issue.milestone.title, project_milestone_path
- - else
- None
+ .col-md-6.text-right
+ %strong.append-right-10
+ Milestone:
+ - if can?(current_user, :modify_issue, @issue)
+ = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
+ = hidden_field_tag :issue_context
+ = f.submit class: 'btn'
+ - elsif issue.milestone
+ = link_to issue.milestone.title, project_milestone_path
+ - else
+ None
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 124eb53571..b6d3a8edf4 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
Issue ##{@issue.iid}
- %span.pull-right
+ %span.pull-right.issue-btn-group
- 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
%i.icon-plus
@@ -16,28 +16,29 @@
%i.icon-edit
Edit
-.votes-holder
- #votes= render 'votes/votes_block', votable: @issue
+.clearfix
+ .votes-holder
+ #votes= render 'votes/votes_block', votable: @issue
-.back-link
- = link_to project_issues_path(@project) do
- ← To issues list
- %span.milestone-nav-link
- - if @issue.milestone
- |
- %span.light Milestone
- = link_to project_milestone_path(@project, @issue.milestone) do
- = @issue.milestone.title
+ .back-link
+ = link_to project_issues_path(@project) do
+ ← To issues list
+ %span.milestone-nav-link
+ - if @issue.milestone
+ |
+ %span.light Milestone
+ = link_to project_milestone_path(@project, @issue.milestone) do
+ = @issue.milestone.title
.issue-box{ class: issue_box_class(@issue) }
- .state
- %span.state-label
+ .state.clearfix
+ .state-label.col-sm-2.col-xs-12
- if @issue.closed?
Closed
- else
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)}
%h4.title
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index 0fe2d1d980..ddff3dbead 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -14,33 +14,6 @@
- @merge_request.errors.full_messages.each do |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
-
- = 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
-
- = 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
.form-group
= f.label :title, class: 'control-label' do
@@ -51,6 +24,23 @@
.col-sm-10
= 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'}.
+ %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)
+
+ = 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'})
+
.form-actions
- if @merge_request.new_record?
@@ -66,20 +56,7 @@
:javascript
disableButtonIfEmptyField("#merge_request_title", ".btn-save");
-
- 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() });
- });
- target_branch.on("change", function() {
- $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
+ $('.assign-to-me-link').on('click', function(e){
+ $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
+ e.preventDefault();
});
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 980ac12674..d1cab89a35 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -11,13 +11,9 @@
- if merge_request.for_fork?
%span.light
#{merge_request.source_project_namespace}:
- = merge_request.source_branch
- %i.icon-angle-right.light
- = merge_request.target_branch
- - else
- = merge_request.source_branch
- %i.icon-angle-right.light
- = merge_request.target_branch
+ = truncate merge_request.source_branch, length: 25
+ %i.icon-angle-right.light
+ = merge_request.target_branch
.merge-request-info
- if merge_request.author
authored by #{link_to_member(merge_request.source_project, merge_request.author)}
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
new file mode 100644
index 0000000000..a8b774a3cd
--- /dev/null
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -0,0 +1,84 @@
+%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? })
+
+ = 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? })
+
+ = 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
+
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
new file mode 100644
index 0000000000..b5479be708
--- /dev/null
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -0,0 +1,82 @@
+%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)
+
+ = 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();
+ });
diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml
index 693c2057a0..8372afa61b 100644
--- a/app/views/projects/merge_requests/branch_from.js.haml
+++ b/app/views/projects/merge_requests/branch_from.js.haml
@@ -1,7 +1,2 @@
:plain
$(".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}");
- }
diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml
index 8ee0e1a8d4..c24e591672 100644
--- a/app/views/projects/merge_requests/new.html.haml
+++ b/app/views/projects/merge_requests/new.html.haml
@@ -1,3 +1,4 @@
-%h3.page-title New Merge Request
-%hr
-= render 'form'
+- if @commits.present?
+ = render 'new_submit'
+- else
+ = render 'new_compare'
diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml
index 2bd850426a..5c6734fd24 100644
--- a/app/views/projects/merge_requests/show/_context.html.haml
+++ b/app/views/projects/merge_requests/show/_context.html.haml
@@ -1,22 +1,24 @@
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
- %strong.append-right-10
- Assignee:
+ .row
+ .col-md-6
+ %strong.append-right-10
+ Assignee:
- - 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)
- - elsif merge_request.assignee
- = link_to_member(@project, @merge_request.assignee)
- - else
- None
+ - 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)
+ - elsif merge_request.assignee
+ = link_to_member(@project, @merge_request.assignee)
+ - else
+ None
- .pull-right
- %strong.append-right-10
- Milestone:
- - 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'})
- = hidden_field_tag :merge_request_context
- = f.submit class: 'btn'
- - elsif merge_request.milestone
- = link_to merge_request.milestone.title, project_milestone_path
- - else
- None
+ .col-md-6.text-right
+ %strong.append-right-10
+ Milestone:
+ - if can?(current_user, :modify_merge_request, @merge_request)
+ = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
+ = hidden_field_tag :merge_request_context
+ = f.submit class: 'btn'
+ - elsif merge_request.milestone
+ = link_to merge_request.milestone.title, project_milestone_path
+ - else
+ None
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index 8855982a2e..435e916c6d 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -1,6 +1,6 @@
.issue-box{ class: issue_box_class(@merge_request) }
- .state
- %span.state-label
+ .state.clearfix
+ %span.state-label.col-sm-2.col-xs-12
- if @merge_request.merged?
Merged
- elsif @merge_request.closed?
@@ -8,7 +8,7 @@
- else
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)}
%h4.title
diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml
index c175d2f6b4..507a9e507f 100644
--- a/app/views/projects/merge_requests/show/_mr_ci.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml
@@ -26,4 +26,4 @@
.ci_widget.ci-error{style: "display:none"}
%i.icon-remove
- %strong Cannot connect to CI server. Please check your setting
+ %strong Cannot connect to the CI server. Please check your settings and try again.
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index 7676fc137c..2c905413bc 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
= "Merge Request ##{@merge_request.iid}"
- %span.pull-right
+ %span.pull-right.issue-btn-group
- if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open?
.btn-group.pull-left
@@ -39,4 +39,4 @@
- else
%span= @merge_request.source_branch
→
- %spanh= @merge_request.target_branch
+ %span= @merge_request.target_branch
diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml
index c9ecbceaf5..80fe540489 100644
--- a/app/views/projects/merge_requests/show/_state_widget.html.haml
+++ b/app/views/projects/merge_requests/show/_state_widget.html.haml
@@ -21,14 +21,6 @@
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
= 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?
%h4 Nothing to merge
%p
@@ -38,3 +30,12 @@
%span.label-branch #{@merge_request.target_branch}
%br
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)
+
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 06cf994678..5c5df46d33 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -100,7 +100,7 @@
%ul.bordered-list
- @users.each do |user|
%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"
%strong= truncate(user.name, lenght: 40)
%br
diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
index 2012aa021b..399ce30d1a 100644
--- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
+++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
@@ -1,34 +1,33 @@
- note1 = notes1.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
- -# Check if line want not changed since comment was left
- /- if !defined?(line1) || line1 == note1.diff_line
- if note1
+ %td.notes_line
+ %span.btn.disabled
+ %i.icon-comment
+ = notes1.count
%td.notes_content
%ul.notes{ rel: note1.discussion_id }
= render notes1
+
= render "projects/notes/discussion_reply_button", note: note1
- %td.notes_line2
- %span.btn.disabled.parallel-comment
- %i.icon-comment
- = notes1.count
- else
%td= ""
%td= ""
- %td= ""
-
- -# Check if line want not changed since comment was left
- /- if !defined?(line2) || line2 == note2.diff_line
- if note2
%td.notes_line
- %span.btn.disabled.parallel-comment
+ %span.btn.disabled
%i.icon-comment
= notes2.count
%td.notes_content
%ul.notes{ rel: note2.discussion_id }
= render notes2
+
= render "projects/notes/discussion_reply_button", note: note2
- else
%td= ""
%td= ""
+
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 3bd592e398..052661962e 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -7,4 +7,4 @@
= render "projects/notes/form"
:javascript
- new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json})
+ new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index 8cb7fa8aa0..1ce292a02d 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -9,6 +9,6 @@
%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)
%p.hint
- Please don't use spaces and slashes
+ Please don't use spaces.
.modal-footer
= link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
diff --git a/bin/pkgr_before_precompile.sh b/bin/pkgr_before_precompile.sh
new file mode 100755
index 0000000000..283abb6a0c
--- /dev/null
+++ b/bin/pkgr_before_precompile.sh
@@ -0,0 +1,23 @@
+#!/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
diff --git a/config/environments/production.rb b/config/environments/production.rb
index ad3c03d8fc..47f7e17aeb 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -53,7 +53,7 @@ Gitlab::Application.configure do
else
"redis://localhost:6379"
end
- config.cache_store = :redis_store, resque_url
+ config.cache_store = :redis_store, resque_url, {namespace: 'cache:gitlab'}
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index d07f5d1fef..e4a67be428 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -19,6 +19,11 @@ production: &base
port: 80
https: false
+ # Uncommment this line below if your ssh host is different from HTTP/HTTPS one
+ # (you'd obviously need to replace ssh.host_example.com with your own host).
+ # Otherwise, ssh host will be set to the `host:` value above
+ # ssh_host: ssh.host_example.com
+
# 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.
# Note that four settings need to be changed for this to work.
@@ -102,7 +107,7 @@ production: &base
# ## :id - Issue id (from commit messages)
# issues_url: "http://redmine.sample/issues/:id"
#
- # ## If not nil, linkis to creating new issues will be replaced with this
+ # ## If not nil, links to creating new issues will be replaced with this
# ## Use placeholders:
# ## :project_id - GitLab project identifier
# ## :issues_tracker_id - Project Name or Id in external issue tracker
@@ -117,6 +122,7 @@ production: &base
## Gravatar
gravatar:
enabled: true # Use user avatar image from Gravatar.com (default: true)
+ # gravatar urls: possible placeholders: %{hash} %{size} %{email}
# plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 4e8b3f304d..97f2954640 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -73,6 +73,7 @@ Settings.gitlab['default_projects_limit'] ||= 10
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost'
+Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
@@ -117,7 +118,7 @@ Settings.gitlab_shell['hooks_path'] ||= Settings.gitlab['user_home'] + '/gitla
Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil?
Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil?
Settings.gitlab_shell['repos_path'] ||= Settings.gitlab['user_home'] + '/repositories/'
-Settings.gitlab_shell['ssh_host'] ||= (Settings.gitlab.host || 'localhost')
+Settings.gitlab_shell['ssh_host'] ||= Settings.gitlab.ssh_host
Settings.gitlab_shell['ssh_port'] ||= 22
Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user
Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
index 6875fa74ed..d0065b63e5 100644
--- a/config/initializers/carrierwave.rb
+++ b/config/initializers/carrierwave.rb
@@ -18,4 +18,16 @@ if File.exists?(aws_file)
config.fog_authenticated_url_expiration = 1 << 29 # optional time (in seconds) that authenticated urls will be valid.
# when fog_public is false and provider is AWS or Google, defaults to 600
end
+
+ # Mocking Fog requests, based on: https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Test-Fog-based-uploaders
+ if Rails.env.test?
+ Fog.mock!
+ connection = ::Fog::Storage.new(
+ :aws_access_key_id => AWS_CONFIG['access_key_id'],
+ :aws_secret_access_key => AWS_CONFIG['secret_access_key'],
+ :provider => 'AWS',
+ :region => AWS_CONFIG['region']
+ )
+ connection.directories.create(:key => AWS_CONFIG['bucket'])
+ end
end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 50669ece7a..d5cb110e88 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -223,6 +223,7 @@ Devise.setup do |config|
method: Gitlab.config.ldap['method'],
bind_dn: Gitlab.config.ldap['bind_dn'],
password: Gitlab.config.ldap['password'],
+ filter: Gitlab.config.ldap['user_filter'],
name_proc: email_stripping_proc
end
@@ -244,4 +245,4 @@ Devise.setup do |config|
config.omniauth provider['name'].to_sym, *provider_arguments
end
-end
+end
\ No newline at end of file
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index f80b67a554..5fe5270236 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -2,7 +2,7 @@
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
- servers: Gitlab::Application.config.cache_store.last, # re-use the Redis config from the Rails cache store
+ servers: Gitlab::Application.config.cache_store[1], # re-use the Redis config from the Rails cache store
key: '_gitlab_session',
secure: Gitlab.config.gitlab.https,
httponly: true,
diff --git a/config/routes.rb b/config/routes.rb
index 52e19e6e43..045042349f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -201,7 +201,9 @@ Gitlab::Application.routes.draw do
resources :blob, only: [:show, :destroy], constraints: {id: /.+/}
resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
- resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
+ resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
+ post :preview, on: :member
+ end
resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
@@ -218,7 +220,7 @@ Gitlab::Application.routes.draw do
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
get :pages
put ':id' => 'wikis#update'
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
index 9303ab9330..164bb63780 100644
--- a/db/fixtures/development/04_project.rb
+++ b/db/fixtures/development/04_project.rb
@@ -40,7 +40,8 @@ Gitlab::Seeder.quiet do
import_url: url,
namespace_id: group.id,
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
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index cb08a7c253..62fd0d84ea 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -1,45 +1,33 @@
Gitlab::Seeder.quiet do
- (1..100).each do |i|
- # Random Project
- project = Project.all.sample
+ Project.all.reject(&:empty_repo?).each do |project|
+ branches = project.repository.branch_names
- # Random user
- user = project.team.users.sample
+ branches.each do |branch_name|
+ break if branches.size < 2
+ source_branch = branches.pop
+ target_branch = branches.pop
- next unless user
+ # Random user
+ user = project.team.users.sample
+ next unless user
- next if project.empty_repo?
+ params = {
+ source_branch: source_branch,
+ target_branch: target_branch,
+ title: Faker::Lorem.sentence(6),
+ description: Faker::Lorem.sentences(3).join(" ")
+ }
- branches = project.repository.branch_names.sample(2)
+ merge_request = MergeRequests::CreateService.new(project, user, params).execute
- next if branches.uniq.size < 2
-
- user_id = user.id
-
- Gitlab::Seeder.by_user(user) do
- MergeRequest.seed(:id, [{
- id: i,
- source_branch: branches.first,
- target_branch: branches.last,
- source_project_id: project.id,
- target_project_id: project.id,
- author_id: user_id,
- assignee_id: user_id,
- milestone: project.milestones.sample,
- title: Faker::Lorem.sentence(6)
- }])
+ if merge_request.valid?
+ merge_request.assignee = user
+ merge_request.milestone = project.milestones.sample
+ merge_request.save
+ print '.'
+ else
+ print 'F'
+ end
end
- print('.')
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
diff --git a/db/migrate/20140415124820_limits_to_mysql.rb b/db/migrate/20140415124820_limits_to_mysql.rb
new file mode 100644
index 0000000000..3f6e62617c
--- /dev/null
+++ b/db/migrate/20140415124820_limits_to_mysql.rb
@@ -0,0 +1 @@
+require_relative 'limits_to_mysql'
diff --git a/db/migrate/20140416074002_add_index_on_iid.rb b/db/migrate/20140416074002_add_index_on_iid.rb
new file mode 100644
index 0000000000..85269e2a03
--- /dev/null
+++ b/db/migrate/20140416074002_add_index_on_iid.rb
@@ -0,0 +1,32 @@
+class AddIndexOnIid < ActiveRecord::Migration
+ def change
+ RemoveDuplicateIid.clean(Issue)
+ RemoveDuplicateIid.clean(MergeRequest, 'target_project_id')
+ RemoveDuplicateIid.clean(Milestone)
+
+ add_index :issues, [:project_id, :iid], unique: true
+ add_index :merge_requests, [:target_project_id, :iid], unique: true
+ add_index :milestones, [:project_id, :iid], unique: true
+ end
+end
+
+class RemoveDuplicateIid
+ def self.clean(klass, project_field = 'project_id')
+ duplicates = klass.find_by_sql("SELECT iid, #{project_field} FROM #{klass.table_name} GROUP BY #{project_field}, iid HAVING COUNT(*) > 1")
+
+ duplicates.each do |duplicate|
+ project_id = duplicate.send(project_field)
+ iid = duplicate.iid
+ items = klass.of_projects(project_id).where(iid: iid)
+
+ if items.size > 1
+ puts "Remove #{klass.name} duplicates for iid: #{iid} and project_id: #{project_id}"
+ items.shift
+ items.each do |item|
+ item.destroy
+ puts '.'
+ end
+ end
+ end
+ end
+end
diff --git a/db/migrate/20140416185734_index_on_current_sign_in_at.rb b/db/migrate/20140416185734_index_on_current_sign_in_at.rb
new file mode 100644
index 0000000000..0bf80ce154
--- /dev/null
+++ b/db/migrate/20140416185734_index_on_current_sign_in_at.rb
@@ -0,0 +1,5 @@
+class IndexOnCurrentSignInAt < ActiveRecord::Migration
+ def change
+ add_index :users, :current_sign_in_at
+ end
+end
diff --git a/db/migrate/20140428105831_add_notes_index_updated_at.rb b/db/migrate/20140428105831_add_notes_index_updated_at.rb
new file mode 100644
index 0000000000..6c25570f12
--- /dev/null
+++ b/db/migrate/20140428105831_add_notes_index_updated_at.rb
@@ -0,0 +1,5 @@
+class AddNotesIndexUpdatedAt < ActiveRecord::Migration
+ def change
+ add_index :notes, :updated_at
+ end
+end
diff --git a/db/migrate/20140502115131_add_repo_size_to_db.rb b/db/migrate/20140502115131_add_repo_size_to_db.rb
new file mode 100644
index 0000000000..7361d1a944
--- /dev/null
+++ b/db/migrate/20140502115131_add_repo_size_to_db.rb
@@ -0,0 +1,5 @@
+class AddRepoSizeToDb < ActiveRecord::Migration
+ def change
+ add_column :projects, :repository_size, :float, default: 0
+ end
+end
diff --git a/db/migrate/20140502125220_migrate_repo_size.rb b/db/migrate/20140502125220_migrate_repo_size.rb
new file mode 100644
index 0000000000..eed6d36681
--- /dev/null
+++ b/db/migrate/20140502125220_migrate_repo_size.rb
@@ -0,0 +1,21 @@
+class MigrateRepoSize < ActiveRecord::Migration
+ def up
+ Project.reset_column_information
+ Project.find_each(batch_size: 500) do |project|
+ begin
+ if project.empty_repo?
+ print '-'
+ else
+ project.update_repository_size
+ print '.'
+ end
+ rescue
+ print 'F'
+ end
+ end
+ puts 'Done'
+ end
+
+ def down
+ end
+end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
new file mode 100644
index 0000000000..2b7afae6d7
--- /dev/null
+++ b/db/migrate/limits_to_mysql.rb
@@ -0,0 +1,10 @@
+class LimitsToMysql < ActiveRecord::Migration
+ def up
+ return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
+
+ change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
+ change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
+ change_column :snippets, :content, :text, limit: 2147483647
+ change_column :notes, :st_diff, :text, limit: 2147483647
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b4b084e60f..a6f883914e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20140414131055) do
+ActiveRecord::Schema.define(version: 20140502125220) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree
add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree
add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
+ add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree
add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
@@ -162,6 +163,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree
add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
+ add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
create_table "milestones", force: true do |t|
@@ -176,6 +178,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
end
add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
+ add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
create_table "namespaces", force: true do |t|
@@ -218,6 +221,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
+ add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
create_table "project_group_links", force: true do |t|
t.integer "project_id", null: false
@@ -247,6 +251,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
t.integer "visibility_level", default: 0, null: false
t.boolean "archived", default: false, null: false
t.string "import_status"
+ t.float "repository_size", default: 0.0
end
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
@@ -348,7 +353,6 @@ ActiveRecord::Schema.define(version: 20140414131055) do
t.integer "notification_level", default: 1, null: false
t.datetime "password_expires_at"
t.integer "created_by_id"
- t.datetime "last_credential_check_at"
t.string "avatar"
t.string "confirmation_token"
t.datetime "confirmed_at"
@@ -356,11 +360,13 @@ ActiveRecord::Schema.define(version: 20140414131055) do
t.string "unconfirmed_email"
t.boolean "hide_no_ssh_key", default: false
t.string "website_url", default: "", null: false
+ t.datetime "last_credential_check_at"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
+ add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree
diff --git a/doc/api/README.md b/doc/api/README.md
index b1740f3579..4c40589fd4 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -13,7 +13,7 @@
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
-+ [Notes](notes.md)
++ [Notes](notes.md) (comments)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
@@ -21,9 +21,11 @@
## Clients
+ [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
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
++ [node-gitlab](https://github.com/moul/node-gitlab) - Node.js
## Introduction
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 823b72f5b0..d18506f9ce 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -193,3 +193,7 @@ Parameters:
+ `id` (required) - The project ID
+ `issue_id` (required) - The ID of the issue
+
+## Comments on issues
+
+Comments are done via the notes resource.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 4e864ae107..d5b106729c 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -105,10 +105,11 @@ POST /projects/:id/merge_requests
Parameters:
+ `id` (required) - The ID of a project
-+ `source_branch` (required) - The source branch
-+ `target_branch` (required) - The target branch
-+ `assignee_id` (optional) - Assignee user ID
-+ `title` (required) - Title of MR
++ `source_branch` (required) - The source branch
++ `target_branch` (required) - The target branch
++ `assignee_id` (optional) - Assignee user ID
++ `title` (required) - Title of MR
++ `target_project_id` (optional) - The target project (numeric id)
```json
{
@@ -257,3 +258,7 @@ Parameters:
}
]
```
+
+## Comments on issues
+
+Comments are done via the notes resource.
diff --git a/doc/api/notes.md b/doc/api/notes.md
index b15ebdd2ba..e9ad6e00c7 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -1,3 +1,5 @@
+Notes can be wall notes or comments on snippets, issues or merge requests.
+
## Wall
### List project wall notes
diff --git a/doc/api/users.md b/doc/api/users.md
index 2d5dedb3a3..2b927c3077 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -220,6 +220,18 @@ Parameters:
+ **none**
+## List SSH keys for user
+
+Get a list of a specified user's SSH keys. Available only for admin
+
+```
+GET /users/:uid/keys
+```
+
+Parameters:
+
++ `uid` (required) - id of specified user
+
## Single SSH key
@@ -286,3 +298,18 @@ Parameters:
+ `id` (required) - SSH key ID
+## Delete SSH key
+
+Deletes key owned by a specified user. Available only for admin.
+
+```
+DELETE /users/:uid/keys/:id
+```
+
+Parameters:
+
++ `uid` (required) - id of specified user
++ `id` (required) - SSH key ID
+
+Will return `200 Ok` on success, or `404 Not found` if either user or key cannot be found.
+
diff --git a/doc/development/README.md b/doc/development/README.md
index aa59eb2c3e..eb88b6c860 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -1,2 +1,5 @@
-+ [Architecture](architecture.md)
-+ [Shell commands](shell_commands.md)
+## Development
+
++ [Architecture](architecture.md) of GitLab
++ [Shell commands](shell_commands.md) in the GitLab codebase
++ [Rake tasks](rake_tasks.md) for development
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
new file mode 100644
index 0000000000..9e75b3a627
--- /dev/null
+++ b/doc/development/rake_tasks.md
@@ -0,0 +1,25 @@
+# Rake tasks for developers
+
+## Setup db with developer seeds:
+
+Note that if your db user does not have advanced privilegies you must create db manually before run this command
+
+```
+bundle exec rake setup
+```
+
+## Run tests
+
+This runs all test suite present in GitLab
+
+```
+bundle exec rake test
+```
+
+## Generate searchable docs for source code
+
+You can find results under `doc/code` directory
+
+```
+bundle exec rake gitlab:generate_docs
+```
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 8604270213..7385324505 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -1,10 +1,10 @@
# 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).

-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
@@ -27,10 +27,9 @@ The GitLab installation consists of setting up the following components:
1. Packages / Dependencies
2. Ruby
3. System Users
-4. GitLab shell
-5. Database
-6. GitLab
-7. Nginx
+4. Database
+5. GitLab
+6. Nginx
# 1. Packages / Dependencies
@@ -119,32 +118,10 @@ Create a `git` user for Gitlab:
sudo adduser --disabled-login --gecos 'GitLab' git
-
-# 4. GitLab shell
-
-GitLab Shell is an ssh access and repository management software developed specially for GitLab.
-
- # Go to home directory
- cd /home/git
-
- # Clone gitlab shell
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.9.3
-
- cd gitlab-shell
-
- sudo -u git -H cp config.yml.example config.yml
-
- # Edit config and replace gitlab_url
- # with something like 'http://domain.com/'
- sudo -u git -H editor config.yml
-
- # Do setup
- sudo -u git -H ./bin/install
-
-
-# 5. Database
+# 4. Database
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
sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev
@@ -153,7 +130,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
sudo -u postgres psql -d template1
# Create a user for GitLab.
- template1=# CREATE USER git;
+ template1=# CREATE USER git CREATEDB;
# Create the GitLab production database & grant all privileges on database
template1=# CREATE DATABASE gitlabhq_production OWNER git;
@@ -165,7 +142,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
sudo -u git -H psql -d gitlabhq_production
-# 6. GitLab
+# 5. GitLab
# We'll install GitLab into home directory of the user "git"
cd /home/git
@@ -276,6 +253,18 @@ that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
# When done you see 'Administrator account created:'
+## Install GitLab shell
+
+GitLab Shell is an ssh access and repository management software developed specially for GitLab.
+
+ # Go to the Gitlab installation folder:
+ cd /home/git/gitlab
+
+ # 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 RAILS_ENV=production
+
+ # 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
## Install Init Script
@@ -313,7 +302,8 @@ Check if GitLab and its environment are configured correctly:
# or
sudo /etc/init.d/gitlab restart
-# 7. Nginx
+
+# 6. Nginx
**Note:**
Nginx is the officially supported web server for GitLab. If you cannot or do not want to use Nginx as your web server, have a look at the
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 62e21dc72b..0fe015b2d3 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -74,6 +74,9 @@ 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.
+## Database
+
+If you want to run the database separately, the **recommended** database size is **1 MB per user**
# Supported webbrowsers
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 3e8d329d55..8318113ce9 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -6,3 +6,4 @@ See the documentation below for details on how to configure these services.
+ [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
+ [LDAP](ldap.md) Set up sign in via LDAP
+ [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth.
++ [Slack](slack.md) Integrate with the Slack chat service
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index a4491432ca..84a5a8e8c2 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -21,6 +21,7 @@ Before configuring individual OmniAuth providers there are a few global settings
```
2. Find the section dealing with OmniAuth. The section will look similar to the following.
+
```
## OmniAuth settings
omniauth:
@@ -50,6 +51,7 @@ Before configuring individual OmniAuth providers there are a few global settings
# app_secret: 'YOUR APP SECRET',
# args: { scope: 'user:email' } }
```
+
3. Change `enabled` to `true`.
4. Consider the next two configuration options: `allow_single_sign_on` and `block_auto_created_users`.
* `allow_single_sign_on` defaults to `false`. If `false` users must be created manually or they will not be able to
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index ac4bdefddd..9be6423f66 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -27,7 +27,7 @@ If a user is a GitLab administrator they receive all permissions.
|Remove protected branches| |||✓|✓|
|Edit project| |||✓|✓|
|Add Deploy Keys to project| |||✓|✓|
-|Confiure Project Hooks| |||✓|✓|
+|Configure Project Hooks| |||✓|✓|
|Switch visibility level| ||||✓|
|Transfer project to another namespace| ||||✓|
|Remove project| ||||✓|
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index bf9d2784af..76d83e6f3b 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -4,7 +4,7 @@ Internal projects will only be available to authenticated users.
#### Public projects
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.
#### Internal projects
diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md
index 9aa80af12c..6be24f0102 100644
--- a/doc/raketasks/README.md
+++ b/doc/raketasks/README.md
@@ -1,6 +1,7 @@
+ [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md)
+ [Features](features.md)
-+ [Maintenance](maintenance.md)
++ [Maintenance](maintenance.md) and self-checks
+ [User management](user_management.md)
+ [Web hooks](web_hooks.md)
++ [Import](import.md) of git repositories in bulk
diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md
new file mode 100644
index 0000000000..e11328dc5c
--- /dev/null
+++ b/doc/raketasks/import.md
@@ -0,0 +1,28 @@
+### Import bare repositories into GitLab project instance
+
+Notes:
+
+* project owner will be a first admin
+* groups will be created as needed
+* group owner will be the first admin
+* existing projects will be skipped
+
+How to use:
+
+1. copy your bare repos under git repos_path (see `config/gitlab.yml` gitlab_shell -> repos_path)
+2. run the command below
+
+```
+bundle exec rake gitlab:import:repos RAILS_ENV=production
+```
+
+Example output:
+
+```
+Processing abcd.git
+ * Created abcd (abcd.git)
+Processing group/xyz.git
+ * Created Group group (2)
+ * Created xyz (group/xyz.git)
+[...]
+```
diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md
index 3033d8c46b..2783c4153c 100644
--- a/doc/raketasks/maintenance.md
+++ b/doc/raketasks/maintenance.md
@@ -110,32 +110,3 @@ If necessary, remove the `tmp/repo_satellites` directory and rerun the command b
```
bundle exec rake gitlab:satellites:create RAILS_ENV=production
```
-
-### Import bare repositories into GitLab project instance
-
-Notes:
-
-* project owner will be a first admin
-* groups will be created as needed
-* group owner will be the first admin
-* existing projects will be skipped
-
-How to use:
-
-1. copy your bare repos under git repos_path (see `config/gitlab.yml` gitlab_shell -> repos_path)
-2. run the command below
-
-```
-bundle exec rake gitlab:import:repos RAILS_ENV=production
-```
-
-Example output:
-
-```
-Processing abcd.git
- * Created abcd (abcd.git)
-Processing group/xyz.git
- * Created Group group (2)
- * Created xyz (group/xyz.git)
-[...]
-```
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index 284e4e1659..e47117c7a0 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -84,8 +84,9 @@ After making the release branch new commits are cherry-picked from master. When
- 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)
-* 24-end of month: release GitLab EE and GitLab CI
+* 25th: release GitLab CI
# Write a blog post
diff --git a/doc/update/6.0-to-6.7.md b/doc/update/6.0-to-6.8.md
similarity index 84%
rename from doc/update/6.0-to-6.7.md
rename to doc/update/6.0-to-6.8.md
index aa1b388fa9..5c71e99aa5 100644
--- a/doc/update/6.0-to-6.7.md
+++ b/doc/update/6.0-to-6.8.md
@@ -1,4 +1,4 @@
-# From 6.0 to 6.7
+# From 6.0 to 6.8
# In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
@@ -33,7 +33,7 @@ sudo -u git -H git fetch --all
For Gitlab Community Edition:
```bash
-sudo -u git -H git checkout 6-7-stable
+sudo -u git -H git checkout 6-8-stable
```
OR
@@ -41,7 +41,7 @@ OR
For GitLab Enterprise Edition:
```bash
-sudo -u git -H git checkout 6-7-stable-ee
+sudo -u git -H git checkout 6-8-stable-ee
```
@@ -57,7 +57,7 @@ sudo apt-get install logrotate
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v1.9.1 # Addresses multiple critical security vulnerabilities
+sudo -u git -H git checkout v1.9.3 # Addresses multiple critical security vulnerabilities
```
### 5. Install libs, migrations, etc.
@@ -90,11 +90,12 @@ sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
TIP: to see what changed in gitlab.yml.example in this release use next command:
```
-git diff 6-0-stable:config/gitlab.yml.example 6-7-stable:config/gitlab.yml.example
+git diff 6-0-stable:config/gitlab.yml.example 6-8-stable:config/gitlab.yml.example
```
-* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/unicorn.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-8-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-8-stable/config/unicorn.rb.example but with your settings.
+* Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-8-stable/lib/support/nginx/gitlab but with your settings.
* Copy rack attack middleware config
```bash
diff --git a/doc/update/6.7-to-6.8.md b/doc/update/6.7-to-6.8.md
index 63023fd384..457433c648 100644
--- a/doc/update/6.7-to-6.8.md
+++ b/doc/update/6.7-to-6.8.md
@@ -62,6 +62,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
# Update init.d script
sudo cp lib/support/init.d/gitlab /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
@@ -92,19 +93,12 @@ If you are using HTTPS, disable gzip as in [this commit](https://gitlab.com/gitl
To improve performance, enable gzip asset compression as seen [in this commit](https://gitlab.com/gitlab-org/gitlab-ce/commit/8af94ed75505f0253823b9b2d44320fecea5b5fb).
-### 6. Update Init script
-
-```bash
-sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
-sudo chmod +x /etc/init.d/gitlab
-```
-
-### 7. Start application
+### 6. Start application
sudo service gitlab start
sudo service nginx restart
-### 8. Check application status
+### 7. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 9a324545eb..5b9209d7df 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -1,9 +1,68 @@
-# 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 and import into Postgres 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.
```
+sudo service gitlab stop
+
+# Update /home/git/gitlab/config/database.yml
+
git clone https://github.com/lanyrd/mysql-postgresql-converter.git
cd mysql-postgresql-converter
mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production
python db_converter.py databasename.mysql databasename.psql
psql -f databasename.psql -d gitlabhq_production
+
+sudo service gitlab start
+```
+
+## Converting a GitLab backup file from MySQL to Postgres
+
+GitLab backup files (_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.
```
diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md
index fd45154ac8..72a94f67b3 100644
--- a/doc/update/upgrader.md
+++ b/doc/update/upgrader.md
@@ -46,4 +46,8 @@ If all items are green, then congratulations upgrade is complete!
You've read through the entire guide, and probably did all the steps manually. Here is a one liner for convenience, the next time you upgrade:
- cd /home/git/gitlab; sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; sudo service gitlab stop; sudo -u git -H ruby script/upgrade.rb -y; sudo service gitlab start; sudo service nginx restart; sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```bash
+cd /home/git/gitlab; sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \
+ sudo service gitlab stop; sudo -u git -H ruby script/upgrade.rb -y; sudo service gitlab start; \
+ sudo service nginx restart; sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index f80891e264..5ad0c8a138 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -2,16 +2,16 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss
---
-You can configure web hook to listen for specific events like pushes, issues, merge requests.
-GitLab will send POST request with data to web hook URL.
-Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
+You can configure web hooks to listen for specific events like pushes, issues or merge requests.
+GitLab will send a POST request with data to the web hook URL.
+Web hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
If you send a web hook to an SSL endpoint [the certificate will not be verified](https://gitlab.com/gitlab-org/gitlab-ce/blob/ccd617e58ea71c42b6b073e692447d0fe3c00be6/app/models/web_hook.rb#L35) since many people use self-signed certificates.
---
#### Push events
-Triggered when you push to the repository except pushing tags.
+Triggered when you push to the repository except when pushing tags.
**Request body:**
@@ -84,7 +84,7 @@ Triggered when a new issue is created or an existing issue was updated/closed/re
#### Merge request events
-Triggered when a new merge request is created or an existing merge request was updated/merges/closed.
+Triggered when a new merge request is created or an existing merge request was updated/merged/closed.
**Request body:**
diff --git a/features/project/forked_merge_requests.feature b/features/project/forked_merge_requests.feature
index 2d94b98c90..5832b729de 100644
--- a/features/project/forked_merge_requests.feature
+++ b/features/project/forked_merge_requests.feature
@@ -30,11 +30,10 @@ Feature: Project Forked Merge Requests
Given I visit project "Forked Shop" merge requests page
And I click link "New 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
@javascript
Scenario: Merge request should target fork repository by default
Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request"
- Then the target repository should be the original repository
\ No newline at end of file
+ Then the target repository should be the original repository
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index fd9a2f01a2..a204c3e10c 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -29,3 +29,13 @@ Feature: Project Browse files
Given I click on "Gemfile.lock" file in repo
And I click button "edit"
Then I can edit code
+
+ @javascript
+ Scenario: I can see editing preview
+ Given I click on "Gemfile.lock" file in repo
+ And I click button "edit"
+ And I edit code
+ And I click link "Diff"
+ Then I see diff
+
+
diff --git a/features/project/wiki.feature b/features/project/wiki.feature
index 90eb2b79c6..4a8c771dda 100644
--- a/features/project/wiki.feature
+++ b/features/project/wiki.feature
@@ -45,3 +45,20 @@ Feature: Project Wiki
And I browse to that Wiki page
And I click on the "Pages" button
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
diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb
index 394acd3fe8..706c9babce 100644
--- a/features/steps/dashboard/dashboard.rb
+++ b/features/steps/dashboard/dashboard.rb
@@ -25,7 +25,6 @@ class Dashboard < Spinach::FeatureSteps
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master"
- find("#merge_request_title").value.should == "New design"
end
Given 'user with name "John Doe" joined project "Shop"' do
diff --git a/features/steps/project/browse_files.rb b/features/steps/project/browse_files.rb
index 069086d5ea..7cdd1101ac 100644
--- a/features/steps/project/browse_files.rb
+++ b/features/steps/project/browse_files.rb
@@ -41,6 +41,18 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
page.evaluate_script('editor.getValue()').should == "GitlabFileEditor"
end
+ step 'I edit code' do
+ page.execute_script('editor.setValue("GitlabFileEditor")')
+ end
+
+ step 'I click link "Diff"' do
+ click_link 'Diff'
+ end
+
+ step 'I see diff' do
+ page.should have_css '.line_holder.new'
+ end
+
step 'I click on "new file" link in repo' do
click_link 'new-file-link'
end
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index df69cb7543..3c497638d9 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -53,6 +53,7 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps
find(:select, "merge_request_source_branch", {}).value.should == 'master'
find(:select, "merge_request_target_branch", {}).value.should == 'stable'
+ click_button "Compare branches"
fill_in "merge_request_title", with: "Merge Request On Forked Project"
end
@@ -148,29 +149,19 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps
current_path.should == edit_project_merge_request_path(@project, @merge_request)
page.should have_content "Edit merge request ##{@merge_request.id}"
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
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"
-
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_source_branch", {}).value.should == ""
find(:select, "merge_request_target_branch", {}).value.should == ""
+ click_button "Compare branches"
end
step 'I should see validation errors' do
- page.should have_content "Source branch can't be blank"
- page.should have_content "Target branch can't be blank"
+ page.should have_content "You must select source and target branch"
end
step 'the target repository should be the original repository' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index f42eb6377c..e0aec699a5 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -61,9 +61,10 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end
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 "notes_refactoring", from: "merge_request_target_branch"
+ click_button "Compare branches"
+ fill_in "merge_request_title", with: "Wiki Feature"
click_button "Submit merge request"
end
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index a819ee37d7..96f2505d24 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -86,6 +86,47 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
page.should have_content @page.title
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 ", :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
@project_wiki = ProjectWiki.new(project, current_user)
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 953c6100f8..d54f9371fb 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -24,7 +24,7 @@ module API
# branch (required) - The name of the branch
# Example Request:
# 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] }
not_found!("Branch does not exist") if @branch.nil?
present @branch, with: Entities::RepoObject, project: user_project
@@ -37,7 +37,9 @@ module API
# branch (required) - The name of the branch
# Example Request:
# 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
@branch = user_project.repository.find_branch(params[:branch])
@@ -55,7 +57,9 @@ module API
# branch (required) - The name of the branch
# Example Request:
# 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
@branch = user_project.repository.find_branch(params[:branch])
diff --git a/lib/api/users.rb b/lib/api/users.rb
index ae808b6272..6ed2740c33 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -113,6 +113,45 @@ module API
end
end
+ # Get ssh keys of a specified user. Only available to admin users.
+ #
+ # Parameters:
+ # uid (required) - The ID of a user
+ # Example Request:
+ # GET /users/:uid/keys
+ get ':uid/keys' do
+ authenticated_as_admin!
+ user = User.find_by(id: params[:uid])
+ if user
+ present user.keys, with: Entities::SSHKey
+ else
+ not_found!
+ end
+ end
+
+ # Delete existing ssh key of a specified user. Only available to admin
+ # users.
+ #
+ # Parameters:
+ # uid (required) - The ID of a user
+ # id (required) - SSH Key ID
+ # Example Request:
+ # DELETE /users/:uid/keys/:id
+ delete ':uid/keys/:id' do
+ authenticated_as_admin!
+ user = User.find_by(id: params[:uid])
+ if user
+ begin
+ key = user.keys.find params[:id]
+ key.destroy
+ rescue ActiveRecord::RecordNotFound
+ not_found!
+ end
+ else
+ not_found!
+ end
+ end
+
# Delete user. Available only for admin
#
# Example Request:
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 05814fc78f..28e323fe30 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -101,7 +101,7 @@ module Backup
def 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
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 214d9824ee..6f7c4f7c90 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -10,15 +10,12 @@ module Backup
Project.find_each(batch_size: 1000) do |project|
print " * #{project.path_with_namespace} ... "
- if project.empty_repo?
- puts "[SKIPPED]".cyan
- next
- end
-
# Create namespace dir if missing
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
else
puts "[FAILED]".red
diff --git a/lib/gitlab/diff_parser.rb b/lib/gitlab/diff_parser.rb
index fb27280c4a..14bbb32863 100644
--- a/lib/gitlab/diff_parser.rb
+++ b/lib/gitlab/diff_parser.rb
@@ -4,9 +4,9 @@ module Gitlab
attr_reader :lines, :new_path
- def initialize(diff)
- @lines = diff.diff.lines.to_a
- @new_path = diff.new_path
+ def initialize(lines, new_path = '')
+ @lines = lines
+ @new_path = new_path
end
def each
@@ -18,10 +18,7 @@ module Gitlab
lines_arr.each do |line|
raw_line = line.dup
- next if line.match(/^\-\-\- \/dev\/null/)
- next if line.match(/^\+\+\+ \/dev\/null/)
- next if line.match(/^\-\-\- a/)
- next if line.match(/^\+\+\+ b/)
+ next if filename?(line)
full_line = html_escape(line.gsub(/\n/, ''))
full_line = ::Gitlab::InlineDiff.replace_markers full_line
@@ -53,8 +50,17 @@ module Gitlab
end
end
+ def empty?
+ @lines.empty?
+ end
+
private
+ def filename?(line)
+ line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
+ '--- /tmp/diffy', '+++ /tmp/diffy')
+ end
+
def identification_type(line)
if line[0] == "+"
"new"
diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb
index 1bac93378e..7f1d1cd653 100644
--- a/lib/gitlab/oauth/user.rb
+++ b/lib/gitlab/oauth/user.rb
@@ -65,7 +65,11 @@ module Gitlab
end
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
def username
diff --git a/lib/gitlab/satellite/compare_action.rb b/lib/gitlab/satellite/compare_action.rb
new file mode 100644
index 0000000000..c923bb9c0f
--- /dev/null
+++ b/lib/gitlab/satellite/compare_action.rb
@@ -0,0 +1,53 @@
+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
diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb
index c6e4d3351c..05123ad9c4 100644
--- a/lib/gitlab/satellite/satellite.rb
+++ b/lib/gitlab/satellite/satellite.rb
@@ -84,6 +84,7 @@ module Gitlab
# Clear the working directory
def clear_working_dir!
repo.git.reset(hard: true)
+ repo.git.clean(f: true, d: true, x: true)
end
# Deletes all branches except the parking branch
diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake
index 853994dd67..8b4ccdfc3f 100644
--- a/lib/tasks/gitlab/setup.rake
+++ b/lib/tasks/gitlab/setup.rake
@@ -15,14 +15,7 @@ namespace :gitlab do
end
Rake::Task["db:setup"].invoke
-
- config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
- success = case config["adapter"]
- when /^mysql/ then
- Rake::Task["add_limits_mysql"].invoke
- when "postgresql" then
- end
-
+ Rake::Task["add_limits_mysql"].invoke
Rake::Task["db:seed_fu"].invoke
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 08de0f2dd5..dfc90bb333 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -1,5 +1,64 @@
namespace :gitlab do
namespace :shell do
+ desc "GITLAB | Install or upgrade gitlab-shell"
+ task :install, [:tag, :repo] => :environment do |t, args|
+ warn_user_is_not_gitlab
+
+ args.with_defaults(tag: "v1.9.3", repo: "https://gitlab.com/gitlab-org/gitlab-shell.git")
+
+ user = Settings.gitlab.user
+ home_dir = Settings.gitlab.user_home
+ gitlab_url = Settings.gitlab.url
+ # gitlab-shell requires a / at the end of the url
+ gitlab_url += "/" unless gitlab_url.match(/\/$/)
+ repos_path = Gitlab.config.gitlab_shell.repos_path
+ target_dir = Gitlab.config.gitlab_shell.path
+
+ # Clone if needed
+ unless File.directory?(target_dir)
+ sh "git clone '#{args.repo}' '#{target_dir}'"
+ end
+
+ # Make sure we're on the right tag
+ Dir.chdir(target_dir) do
+ sh "git fetch origin && git reset --hard $(git describe #{args.tag} || git describe origin/#{args.tag})"
+
+ redis_url = URI.parse(ENV['REDIS_URL'] || "redis://localhost:6379")
+
+ config = {
+ user: user,
+ gitlab_url: gitlab_url,
+ http_settings: {self_signed_cert: false}.stringify_keys,
+ repos_path: repos_path,
+ auth_file: File.join(home_dir, ".ssh", "authorized_keys"),
+ redis: {
+ bin: %x{which redis-cli}.chomp,
+ host: redis_url.host,
+ port: redis_url.port,
+ namespace: "resque:gitlab"
+ }.stringify_keys,
+ log_level: "INFO",
+ audit_usernames: false
+ }.stringify_keys
+
+ # Generate config.yml based on existing gitlab settings
+ File.open("config.yml", "w+") {|f| f.puts config.to_yaml}
+
+ # Launch installation process
+ sh "bin/install"
+ end
+
+ # Required for debian packaging with PKGR: Setup .ssh/environment with
+ # the current PATH, so that the correct ruby version gets loaded
+ # Requires to set "PermitUserEnvironment yes" in sshd config (should not
+ # be an issue since it is more than likely that there are no "normal"
+ # user accounts on a gitlab server). The alternative is for the admin to
+ # install a ruby (1.9.3+) in the global path.
+ File.open(File.join(home_dir, ".ssh", "environment"), "w+") do |f|
+ f.puts "PATH=#{ENV['PATH']}"
+ end
+ end
+
desc "GITLAB | Setup gitlab-shell"
task setup: :environment do
setup
diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake
index 2c9b997893..9516210e20 100644
--- a/lib/tasks/gitlab/test.rake
+++ b/lib/tasks/gitlab/test.rake
@@ -8,9 +8,9 @@ namespace :gitlab do
]
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
diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake
index 46b6451752..a1972a682d 100644
--- a/lib/tasks/migrate/add_limits_mysql.rake
+++ b/lib/tasks/migrate/add_limits_mysql.rake
@@ -1,14 +1,7 @@
+require Rails.root.join('db/migrate/limits_to_mysql')
+
desc "GITLAB | Add limits to strings in mysql database"
task add_limits_mysql: :environment do
puts "Adding limits to schema.rb for mysql"
LimitsToMysql.new.up
end
-
-class LimitsToMysql < ActiveRecord::Migration
- def up
- change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
- change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
- change_column :snippets, :content, :text, limit: 2147483647
- change_column :notes, :st_diff, :text, limit: 2147483647
- end
-end
diff --git a/lib/tasks/setup.rake b/lib/tasks/setup.rake
new file mode 100644
index 0000000000..93701de8f6
--- /dev/null
+++ b/lib/tasks/setup.rake
@@ -0,0 +1,4 @@
+desc "GITLAB | Setup gitlab db"
+task :setup do
+ Rake::Task["gitlab:setup"].invoke
+end
diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png
new file mode 100644
index 0000000000..6f2e0dd090
Binary files /dev/null and b/public/apple-touch-icon-precomposed.png differ
diff --git a/public/static.css b/public/static.css
index aa834553a1..c6f92ac01d 100644
--- a/public/static.css
+++ b/public/static.css
@@ -2,7 +2,6 @@ body {
color: #666;
text-align: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- sans-serif;
margin:0;
width: 800px;
margin: auto;
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
new file mode 100644
index 0000000000..4f8a5f909d
--- /dev/null
+++ b/spec/finders/notes_finder_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe NotesFinder do
+ let(:user) { create :user }
+ let(:project) { create :project }
+ let(:note1) { create :note_on_commit, project: project }
+ let(:note2) { create :note_on_commit, project: project }
+ let(:commit) { note1.noteable }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ describe :execute do
+ let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
+
+ before do
+ note1
+ note2
+ end
+
+ it 'should find all notes' do
+ notes = NotesFinder.new.execute(project, user, params)
+ notes.size.should eq(2)
+ end
+
+ it 'should raise an exception for an invalid target_type' do
+ params.merge!(target_type: 'invalid')
+ expect { NotesFinder.new.execute(project, user, params) }.to raise_error('invalid target_type')
+ end
+
+ it 'filters out old notes' do
+ note2.update_attribute(:updated_at, 2.hours.ago)
+ notes = NotesFinder.new.execute(project, user, params)
+ notes.should eq([note1])
+ end
+ end
+end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 61c561335e..0376e0aadf 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -46,7 +46,7 @@ describe ApplicationHelper do
group = create(:group)
group.avatar = File.open(avatar_file_path)
group.save!
- group_icon(group.path).to_s.should == "/uploads/group/avatar/#{ group.id }/gitlab_logo.png"
+ group_icon(group.path).to_s.should match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png")
end
it "should give default avatar_icon when no avatar is present" do
@@ -63,7 +63,7 @@ describe ApplicationHelper do
user = create(:user)
user.avatar = File.open(avatar_file_path)
user.save!
- avatar_icon(user.email).to_s.should == "/uploads/user/avatar/#{ user.id }/gitlab_logo.png"
+ avatar_icon(user.email).to_s.should match("/uploads/user/avatar/#{ user.id }/gitlab_logo.png")
end
it "should call gravatar_icon when no avatar is present" do
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index e86a60a42b..3b07504495 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -239,7 +239,7 @@ describe Notify do
it_behaves_like 'an assignee email'
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
it 'contains a link to the new merge request' do
@@ -275,7 +275,7 @@ describe Notify do
end
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
it 'contains the name of the previous assignee' do
@@ -303,7 +303,7 @@ describe Notify do
end
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
it 'contains the new status' do
@@ -426,7 +426,7 @@ describe Notify do
it_behaves_like 'a note email'
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
it 'contains a link to the merge request note' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 86610c4751..a6d300b099 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -242,6 +242,67 @@ describe API::API, api: true do
end
end
+ describe 'GET /user/:uid/keys' do
+ before { admin }
+
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get api("/users/#{user.id}/keys")
+ response.status.should == 401
+ end
+ end
+
+ context 'when authenticated' do
+ it 'should return 404 for non-existing user' do
+ get api('/users/999999/keys', admin)
+ response.status.should == 404
+ end
+
+ it 'should return array of ssh keys' do
+ user.keys << key
+ user.save
+ get api("/users/#{user.id}/keys", admin)
+ response.status.should == 200
+ json_response.should be_an Array
+ json_response.first['title'].should == key.title
+ end
+ end
+ end
+
+ describe 'DELETE /user/:uid/keys/:id' do
+ before { admin }
+
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ delete api("/users/#{user.id}/keys/42")
+ response.status.should == 401
+ end
+ end
+
+ context 'when authenticated' do
+ it 'should delete existing key' do
+ user.keys << key
+ user.save
+ expect {
+ delete api("/users/#{user.id}/keys/#{key.id}", admin)
+ }.to change { user.keys.count }.by(-1)
+ response.status.should == 200
+ end
+
+ it 'should return 404 error if user not found' do
+ user.keys << key
+ user.save
+ delete api("/users/999999/keys/#{key.id}", admin)
+ response.status.should == 404
+ end
+
+ it 'should return 404 error if key not foud' do
+ delete api("/users/#{user.id}/keys/42", admin)
+ response.status.should == 404
+ end
+ end
+ end
+
describe "DELETE /users/:id" do
before { admin }