Commit a03f59d5 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-6-9' into 'master'

Upstream 6.9.0.pre CE to EE
parents 09ebd837 556ca352
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
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
......
......@@ -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)
......@@ -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'
......
......@@ -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
......
......@@ -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
......
6.8.0-ee
6.9.0.pre-ee
......@@ -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) ->
"<div class='line' id='LC" + (i + 1) + "'>" + line + "</div>"
"<span class='line' id='LC" + (i + 1) + "'>" + line + "</span>"
).join("\n"))
hljs.highlightBlock(e)
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
@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"
......
......@@ -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.
......
......@@ -26,6 +26,10 @@
margin-top: -5px;
}
.left-options {
margin-top: -3px;
}
.file_name {
color: $style_color;
font-size: 14px;
......
......@@ -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;
}
}
......@@ -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 {
padding: 4px 10px;
}
}
.ui-state-default {
border: 1px solid #FFF;
background: #FFF;
color: #777;
}
&:hover {
background: $hover;
border-color: $primary_color;
@include border-radius(0px);
color: #333;
.ui-state-highlight {
border: 1px solid #EEE;
background: #EEE;
}
.ui-state-active {
border: 1px solid $bg_style_color;
background: $bg_style_color;
color: #FFF;
}
.ui-state-hover,
.ui-state-focus {
border: 1px solid $hover;
background: $hover;
color: #333;
}
}
......@@ -47,7 +47,7 @@ a {
text-decoration: underline;
}
&.dark {
&.darken {
color: $style_color;
}
......
......@@ -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);
......
......@@ -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;
tr.line_holder.parallel{
.old_line, .new_line, .diff_line {
min-width: 50px;
}
.diff-side table, td.diff-middle table {
height: 700px;
td.line_content.parallel{
width: 50%;
}
.diff-middle {
width: 114px;
vertical-align: top;
height: 700px;
overflow: hidden
}
.old_line, .new_line, .diff_line {
......
.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;
......
......@@ -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;
}
}
}
}
......@@ -31,7 +31,6 @@
.mr_source_commit,
.mr_target_commit {
margin-top: 10px;
.commit {
margin: 0;
padding: 2px 0;
......
......@@ -139,6 +139,7 @@ ul.notes {
background-color: #fff;
border-width: 1px 0;
padding-top: 0;
vertical-align: top;
li {
padding: 5px;
......
......@@ -40,4 +40,10 @@
.votes-holder {
float: right;
width: 250px;
@media (max-width: $screen-xs-max) {
width: 100%;
margin-top: 5px;
margin-bottom: 10px;
}
}
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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] << {
......
......@@ -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)
......
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
......@@ -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
......
......@@ -16,7 +16,8 @@ module CommitsHelper
end
def each_diff_line(diff, index)
Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old|
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
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......@@ -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
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......@@ -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
......
......@@ -184,7 +184,8 @@ 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|
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
......
......@@ -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
......@@ -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!
......
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -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|
......
......@@ -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'
%p
= "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
%p
= "Merge Request !#{@merge_request.iid} was merged"
= "Merge Request ##{@merge_request.iid} was merged"
= "Merge Request #{@merge_request.iid} was merged"
= "Merge Request ##{@merge_request.iid} was merged"
Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
......@@ -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
......
......@@ -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|
%tr.line_holder.parallel
- if line.type == :file_created
%td.line_content.parallel= "File was created"
- elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%div.diff-middle
%div.text-file
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line=""
- if new_lines[index].type == :added
%td.new_line.new= new_lines[index].num
- else
%td.new_line= new_lines[index].num
%div.diff-side.diff-side-right
%table
- new_lines.each do |line|
- new_line = new_lines[index]
- old_line = old_lines[index]
%tr.line_holder.parallel
- if line.type == :file_deleted
-# For old line
- if old_line.type == :file_created
%td.old_line= old_line.num
%td.line_content.parallel= "File was created"
- elsif old_line.type == :deleted
%td.old_line.old= old_line.num
%td.line_content{class: "parallel noteable_line old #{old_line.code}", "line_code" => old_line.code}= old_line.content
- else old_line.type == :no_change
%td.old_line= old_line.num
%td.line_content.parallel= old_line.content
-# For new line
- if new_line.type == :file_deleted
%td.new_line= new_line.num
%td.line_content.parallel= "File was deleted"
- elsif 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());
});
- elsif new_line.type == :added
%td.new_line.new= new_line.num
%td.line_content{class: "parallel noteable_line new #{new_line.code}", "line_code" => new_line.code}= new_line.content
- else new_line.type == :no_change
%td.new_line= new_line.num
%td.line_content.parallel= new_line.content
- if @reply_allowed
- comments1 = @line_notes.select { |n| n.line_code == old_line.code }.sort_by(&:created_at)
- comments2 = @line_notes.select { |n| n.line_code == new_line.code }.sort_by(&:created_at)
- unless comments1.empty? and comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2
$('.diff-side-left').on('scroll', function(){
$('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
$('.diff-side-right').scrollLeft($(this).scrollLeft());
});
%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" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : 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-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" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : 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.
%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()
}
})
= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
.row
.col-md-6
%strong.append-right-10
Assignee:
......@@ -9,11 +11,11 @@
- else
None
.pull-right
.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 (none):" }, {class: 'select2 select2-compact'})
= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
= hidden_field_tag :issue_context
= f.submit class: 'btn'
- elsif issue.milestone
......
%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,10 +16,11 @@
%i.icon-edit
Edit
.votes-holder
.clearfix
.votes-holder
#votes= render 'votes/votes_block', votable: @issue
.back-link
.back-link
= link_to project_issues_path(@project) do
&larr; To issues list
%span.milestone-nav-link
......@@ -30,14 +31,14 @@
= @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
......
......@@ -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
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.mr_source_commit
%br
.form-group
= label_tag nil, class: 'control-label' do
To
.col-sm-10
.clearfix
.pull-left
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.mr_target_commit
%hr
.merge-request-form-info
.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)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.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();
});
......@@ -11,11 +11,7 @@
- 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
= truncate merge_request.source_branch, length: 25
%i.icon-angle-right.light
= merge_request.target_branch
.merge-request-info
......
%h3.page-title Compare branches for new Merge Request
%hr
= form_for [@project, @merge_request], url: new_project_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline" } do |f|
.hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row
.col-md-6
.panel.panel-default
.panel-heading
%strong Source branch
.panel-body
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? })
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.panel-footer
.mr_source_commit
.col-md-6
.panel.panel-default
.panel-heading
%strong Target branch
.panel-body
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.panel-footer
.mr_target_commit
-if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
- if @merge_request.source_branch.present? && @merge_request.target_branch.present?
.light-well
%center
%h4
There isn't anything to merge.
%p.slead
- if @merge_request.source_branch == @merge_request.target_branch
You'll need to use different branch names to get a valid comparison.
- else
%span.label-branch #{@merge_request.source_branch}
and
%span.label-branch #{@merge_request.target_branch}
are the same.
%hr
= f.submit 'Compare branches', class: "btn btn-primary mr-compare-btn"
:javascript
var source_branch = $("#merge_request_source_branch")
, target_branch = $("#merge_request_target_branch")
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
:coffeescript
$(".merge-request-form").on 'submit', ->
if $("#merge_request_source_branch").val() is "" or $('#merge_request_target_branch').val() is ""
$(".mr-compare-errors").html("You must select source and target branch to proceed")
$(".mr-compare-errors").fadeIn()
event.preventDefault()
return
%h3.page-title
New merge request
%p.slead
From
%strong.monospace
#{@merge_request.source_project_namespace}:#{@merge_request.source_branch}
into
%strong.monospace
#{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
%span.pull-right
= link_to 'Change branches', new_project_merge_request_path(@project)
= form_for [@project, @merge_request], html: { class: "merge-request-form" } do |f|
.panel.panel-default
.panel-body
.form-group
.light
= f.label :title do
= "Title *"
= f.text_field :title, class: "form-control input-lg js-gfm-input", maxlength: 255, rows: 5, required: true
.form-group
.light
= f.label :description, "Description"
= f.text_area :description, class: "form-control js-gfm-input", rows: 10
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.form-group
.issue-assignee
= f.label :assignee_id do
%i.icon-user
Assign to
%div
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id do
%i.icon-time
Milestone
%div= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.panel-footer
- if @target_repo.contribution_guide
- contribution_guide_url = project_blob_path(@target_project, tree_join(@target_repo.root_ref, @target_repo.contribution_guide.name))
%p
Please review the
%strong #{link_to "guidelines for contribution", contribution_guide_url}
to this repository.
= f.hidden_field :source_project_id
= f.hidden_field :target_project_id
= f.hidden_field :target_branch
= f.hidden_field :source_branch
= f.submit 'Submit merge request', class: "btn btn-create"
.mr-compare
%div.ui-box
.title
Commits (#{@commits.count})
- if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
%ul.well-list
- Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE)).each do |commit|
= render "projects/commits/inline_commit", commit: commit, project: @project
%li.warning-row.unstyled
other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Changes
- if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line changes are not shown.
:javascript
$('.assign-to-me-link').on('click', function(e){
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
: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}");
}
%h3.page-title New Merge Request
%hr
= render 'form'
- if @commits.present?
= render 'new_submit'
- else
= render 'new_compare'
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
.row
.col-md-6
%strong.append-right-10
Assignee:
......@@ -9,11 +11,11 @@
- else
None
.pull-right
.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 (none):" }, {class: 'select2 select2-compact'})
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
= hidden_field_tag :merge_request_context
= f.submit class: 'btn'
- elsif merge_request.milestone
......
.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
......
......@@ -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.
%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
&rarr;
%spanh= @merge_request.target_branch
%span= @merge_request.target_branch
......@@ -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)
......@@ -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
......
- 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= ""
......@@ -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})
......@@ -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'
#!/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
......@@ -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"
......
......@@ -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
......
......@@ -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
......
......@@ -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
......@@ -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
......
......@@ -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,
......
......@@ -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'
......
......@@ -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
......
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
branches.each do |branch_name|
break if branches.size < 2
source_branch = branches.pop
target_branch = branches.pop
# Random user
user = project.team.users.sample
next unless user
next if project.empty_repo?
branches = project.repository.branch_names.sample(2)
params = {
source_branch: source_branch,
target_branch: target_branch,
title: Faker::Lorem.sentence(6),
description: Faker::Lorem.sentences(3).join(" ")
}
next if branches.uniq.size < 2
merge_request = MergeRequests::CreateService.new(project, user, params).execute
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
require_relative 'limits_to_mysql'
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
class IndexOnCurrentSignInAt < ActiveRecord::Migration
def change
add_index :users, :current_sign_in_at
end
end
class AddNotesIndexUpdatedAt < ActiveRecord::Migration
def change
add_index :notes, :updated_at
end
end
class AddRepoSizeToDb < ActiveRecord::Migration
def change
add_column :projects, :repository_size, :float, default: 0
end
end
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
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
......@@ -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
......
......@@ -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
......
......@@ -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.
......@@ -109,6 +109,7 @@ Parameters:
+ `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.
Notes can be wall notes or comments on snippets, issues or merge requests.
## Wall
### List project wall notes
......
......@@ -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.
+ [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
# 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
```
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment